@evident-ai/runner-cdk 3.5.2-dev.43244a2 → 3.5.2-dev.4f96188
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.
- package/README.md +82 -40
- package/dist/controller-lambda/handler.js +74 -79
- package/dist/evident-scale-to-zero-construct.js +9 -9
- package/dist/image-version-reporter-lambda/handler.js +12 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -2
- package/dist/microvm/constants.d.ts +2 -0
- package/dist/microvm/constants.js +14 -2
- package/dist/microvm/construct.d.ts +6 -8
- package/dist/microvm/construct.js +26 -39
- package/dist/microvm/controller/{doorbell.d.ts → doorbell-request.d.ts} +6 -6
- package/dist/microvm/controller/{doorbell.js → doorbell-request.js} +2 -2
- package/dist/microvm/controller/handle-doorbell.d.ts +2 -2
- package/dist/microvm/controller/handle-doorbell.js +62 -62
- package/dist/microvm/image/stage-context.d.ts +13 -38
- package/dist/microvm/image/stage-context.js +30 -120
- package/dist/microvm/image-version-reporter/construct.d.ts +1 -1
- package/dist/microvm/image-version-reporter/construct.js +4 -5
- package/dist/microvm/image-version-reporter/handler.d.ts +2 -2
- package/dist/microvm/image-version-reporter/handler.js +7 -7
- package/dist/microvm/shapes.d.ts +10 -21
- package/dist/microvm/shapes.js +10 -17
- package/dist/waker-lambda/handler.js +3 -3
- package/package.json +3 -3
- package/dist/microvm-image-context/Dockerfile +0 -190
- package/dist/microvm-image-context/hook-server.js +0 -286
- package/dist/microvm-image-context/hooks/common.sh +0 -905
- package/dist/microvm-image-context/hooks/resume +0 -81
- package/dist/microvm-image-context/hooks/run +0 -95
- package/dist/microvm-image-context/hooks/suspend +0 -21
- package/dist/microvm-image-context/hooks/terminate +0 -36
|
@@ -39,6 +39,7 @@ const constructs_1 = require("constructs");
|
|
|
39
39
|
const ec2 = __importStar(require("aws-cdk-lib/aws-ec2"));
|
|
40
40
|
const ecs = __importStar(require("aws-cdk-lib/aws-ecs"));
|
|
41
41
|
const iam = __importStar(require("aws-cdk-lib/aws-iam"));
|
|
42
|
+
const constants_1 = require("./microvm/constants");
|
|
42
43
|
const construct_1 = require("./waker/construct");
|
|
43
44
|
/** Current fixed value of `resourcePrefix`, kept as the default so an adopter
|
|
44
45
|
* omitting the prop reproduces today's physical names exactly. */
|
|
@@ -111,15 +112,7 @@ class EvidentScaleToZeroConstruct extends constructs_1.Construct {
|
|
|
111
112
|
GIT_BRANCH: gitBranch ?? 'main',
|
|
112
113
|
// Scale-to-zero self-stop inputs (the entrypoint uses these to scale to 0).
|
|
113
114
|
EVIDENT_IDLE_TIMEOUT_SECONDS: String(idleTimeoutSeconds),
|
|
114
|
-
|
|
115
|
-
// retention rule is set, sweeps OpenCode sessions idle longer than the
|
|
116
|
-
// window (protected/active sessions are never touched). Tightened from 7d
|
|
117
|
-
// to 24h per #537: at 7d, opencode.db was observed growing ~11x (77 MB ->
|
|
118
|
-
// 849 MB) in ~24h, so 7d was far too permissive at the observed growth
|
|
119
|
-
// rate. 24h is the tighter mitigation that came out of that (see also the
|
|
120
|
-
// OPENCODE_VERSION pin above). Sweep runs on the default 1h interval and
|
|
121
|
-
// never touches a protected/active session.
|
|
122
|
-
EVIDENT_SESSION_CLEANUP_MAX_AGE: '24h',
|
|
115
|
+
EVIDENT_SESSION_CLEANUP_MAX_AGE: constants_1.SESSION_CLEANUP_MAX_AGE,
|
|
123
116
|
CLUSTER: cluster.clusterName,
|
|
124
117
|
SERVICE: serviceName,
|
|
125
118
|
EVIDENT_SELFSTOP_ROLE_ARN: selfStopRole.roleArn,
|
|
@@ -164,6 +157,13 @@ class EvidentScaleToZeroConstruct extends constructs_1.Construct {
|
|
|
164
157
|
// Object Get/Put/Delete scoped to this agent's prefix. DeleteObject is
|
|
165
158
|
// required: Litestream's retention/compaction prunes old LTX files.
|
|
166
159
|
replicaBucket.grantReadWrite(taskRole, `${this.replicaPrefix}/*`);
|
|
160
|
+
// Vault credentials are read and conditionally written, so this shared prefix
|
|
161
|
+
// needs no delete or list permission; conditional writes use request headers.
|
|
162
|
+
taskRole.addToPrincipalPolicy(new iam.PolicyStatement({
|
|
163
|
+
sid: 'ReadWriteCredentialVaultObjects',
|
|
164
|
+
actions: ['s3:GetObject', 's3:PutObject'],
|
|
165
|
+
resources: [replicaBucket.arnForObjects('credential-vaults/*')],
|
|
166
|
+
}));
|
|
167
167
|
this.service = new ecs.FargateService(this, 'Service', {
|
|
168
168
|
cluster,
|
|
169
169
|
taskDefinition,
|
|
@@ -29,15 +29,17 @@ var import_client_secrets_manager = require("@aws-sdk/client-secrets-manager");
|
|
|
29
29
|
var PHYSICAL_RESOURCE_ID = "evident-image-version-report";
|
|
30
30
|
var REPORT_TIMEOUT_MS = 5e3;
|
|
31
31
|
var secrets = new import_client_secrets_manager.SecretsManagerClient({});
|
|
32
|
-
async function
|
|
32
|
+
async function fetchControllerSigningSecret(secretArn, secretKey) {
|
|
33
33
|
const { SecretString } = await secrets.send(new import_client_secrets_manager.GetSecretValueCommand({ SecretId: secretArn }));
|
|
34
34
|
if (!SecretString) {
|
|
35
|
-
throw new Error(`
|
|
35
|
+
throw new Error(`controller signing secret ${secretArn} has no SecretString`);
|
|
36
36
|
}
|
|
37
37
|
const parsed = JSON.parse(SecretString);
|
|
38
38
|
const value = parsed !== null && typeof parsed === "object" ? parsed[secretKey] : void 0;
|
|
39
39
|
if (typeof value !== "string" || value === "") {
|
|
40
|
-
throw new Error(
|
|
40
|
+
throw new Error(
|
|
41
|
+
`controller signing secret ${secretArn} is missing a non-empty '${secretKey}' field`
|
|
42
|
+
);
|
|
41
43
|
}
|
|
42
44
|
return value;
|
|
43
45
|
}
|
|
@@ -63,7 +65,7 @@ function response() {
|
|
|
63
65
|
return { PhysicalResourceId: PHYSICAL_RESOURCE_ID };
|
|
64
66
|
}
|
|
65
67
|
var runtimeDependencies = {
|
|
66
|
-
getSecret:
|
|
68
|
+
getSecret: fetchControllerSigningSecret,
|
|
67
69
|
fetch: globalThis.fetch
|
|
68
70
|
};
|
|
69
71
|
async function handleImageVersionReport(event, getConfig, dependencies = runtimeDependencies) {
|
|
@@ -77,7 +79,10 @@ async function handleImageVersionReport(event, getConfig, dependencies = runtime
|
|
|
77
79
|
versions = readImageVersions(event.ResourceProperties?.imageVersions);
|
|
78
80
|
const config = getConfig();
|
|
79
81
|
runnerId = config.evidentRunnerId;
|
|
80
|
-
const secret = await dependencies.getSecret(
|
|
82
|
+
const secret = await dependencies.getSecret(
|
|
83
|
+
config.controllerSigningSecretArn,
|
|
84
|
+
config.controllerSigningSecretKey
|
|
85
|
+
);
|
|
81
86
|
const body = JSON.stringify({
|
|
82
87
|
type: "runner.microvm_image_versions_reported",
|
|
83
88
|
versions
|
|
@@ -117,8 +122,8 @@ async function handleImageVersionReport(event, getConfig, dependencies = runtime
|
|
|
117
122
|
return response();
|
|
118
123
|
}
|
|
119
124
|
var handler = (event) => handleImageVersionReport(event, () => ({
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
controllerSigningSecretArn: requireEnv("CONTROLLER_SIGNING_SECRET_ARN"),
|
|
126
|
+
controllerSigningSecretKey: requireEnv("CONTROLLER_SIGNING_SECRET_KEY"),
|
|
122
127
|
evidentApiUrl: requireEnv("EVIDENT_API_URL"),
|
|
123
128
|
evidentRunnerId: requireEnv("EVIDENT_RUNNER_ID")
|
|
124
129
|
}));
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export { EvidentWaker, type EvidentWakerProps } from './waker/construct';
|
|
|
3
3
|
export { EvidentMicrovmConstruct, type EvidentMicrovmConstructProps } from './microvm/construct';
|
|
4
4
|
export { MICROVM_SHAPES, validateShapes, type MicrovmShape } from './microvm/shapes';
|
|
5
5
|
export { MICROVM_MAX_RUN_SECONDS, HOOKS_PORT, HOOK_TIMEOUT_SECONDS, } from './microvm/constants';
|
|
6
|
-
export {
|
|
6
|
+
export { MICROVM_WORKSPACE_DIRECTORY, stageMicrovmBuildContext, type StageMicrovmBuildContextOptions, } from './microvm/image/stage-context';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.stageMicrovmBuildContext = exports.MICROVM_WORKSPACE_DIRECTORY = exports.HOOK_TIMEOUT_SECONDS = exports.HOOKS_PORT = exports.MICROVM_MAX_RUN_SECONDS = exports.validateShapes = exports.MICROVM_SHAPES = exports.EvidentMicrovmConstruct = exports.EvidentWaker = exports.EvidentScaleToZeroConstruct = void 0;
|
|
4
4
|
var evident_scale_to_zero_construct_1 = require("./evident-scale-to-zero-construct");
|
|
5
5
|
Object.defineProperty(exports, "EvidentScaleToZeroConstruct", { enumerable: true, get: function () { return evident_scale_to_zero_construct_1.EvidentScaleToZeroConstruct; } });
|
|
6
6
|
var construct_1 = require("./waker/construct");
|
|
@@ -17,4 +17,5 @@ Object.defineProperty(exports, "MICROVM_MAX_RUN_SECONDS", { enumerable: true, ge
|
|
|
17
17
|
Object.defineProperty(exports, "HOOKS_PORT", { enumerable: true, get: function () { return constants_1.HOOKS_PORT; } });
|
|
18
18
|
Object.defineProperty(exports, "HOOK_TIMEOUT_SECONDS", { enumerable: true, get: function () { return constants_1.HOOK_TIMEOUT_SECONDS; } });
|
|
19
19
|
var stage_context_1 = require("./microvm/image/stage-context");
|
|
20
|
-
Object.defineProperty(exports, "
|
|
20
|
+
Object.defineProperty(exports, "MICROVM_WORKSPACE_DIRECTORY", { enumerable: true, get: function () { return stage_context_1.MICROVM_WORKSPACE_DIRECTORY; } });
|
|
21
|
+
Object.defineProperty(exports, "stageMicrovmBuildContext", { enumerable: true, get: function () { return stage_context_1.stageMicrovmBuildContext; } });
|
|
@@ -5,3 +5,5 @@ export declare const RUN_HOOK_PAYLOAD_MAX_BYTES = 16384;
|
|
|
5
5
|
export declare const SUSPENDING_POLL_ATTEMPTS = 4;
|
|
6
6
|
export declare const SUSPENDING_POLL_INTERVAL_MS = 500;
|
|
7
7
|
export declare const MICROVM_MAX_RUN_SECONDS = 28800;
|
|
8
|
+
export declare const SESSION_CLEANUP_MAX_AGE = "24h";
|
|
9
|
+
export declare const CONTROLLER_SIGNING_SECRET_KEY = "CONTROLLER_SIGNING_SECRET";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Shared literals for the MicroVM controller and
|
|
2
|
+
// Shared literals for the MicroVM controller, its image, and the Fargate runner.
|
|
3
3
|
// Every module imports from here rather than restating a value that must match
|
|
4
4
|
// across them. The image template pins matching copies, guarded by
|
|
5
5
|
// `microvm/image/dockerfile.test.ts`.
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.MICROVM_MAX_RUN_SECONDS = exports.SUSPENDING_POLL_INTERVAL_MS = exports.SUSPENDING_POLL_ATTEMPTS = exports.RUN_HOOK_PAYLOAD_MAX_BYTES = exports.HOOK_TIMEOUT_SECONDS = exports.HOOKS_DIR = exports.HOOKS_PORT = void 0;
|
|
7
|
+
exports.CONTROLLER_SIGNING_SECRET_KEY = exports.SESSION_CLEANUP_MAX_AGE = exports.MICROVM_MAX_RUN_SECONDS = exports.SUSPENDING_POLL_INTERVAL_MS = exports.SUSPENDING_POLL_ATTEMPTS = exports.RUN_HOOK_PAYLOAD_MAX_BYTES = exports.HOOK_TIMEOUT_SECONDS = exports.HOOKS_DIR = exports.HOOKS_PORT = void 0;
|
|
8
8
|
// The port the image's hook server binds, baked into the published Dockerfile
|
|
9
9
|
// (`ENV HOOKS_PORT`) and passed to the construct as `hooksPort`. Exported from
|
|
10
10
|
// the package index because a consumer building the published image context
|
|
@@ -32,3 +32,15 @@ exports.SUSPENDING_POLL_INTERVAL_MS = 500;
|
|
|
32
32
|
// longer than any VM can possibly still be running, no VM started before the
|
|
33
33
|
// deactivation can outlive it. The two MUST move together.
|
|
34
34
|
exports.MICROVM_MAX_RUN_SECONDS = 28800;
|
|
35
|
+
// Auto-cleanup (issue #190): `evident run` reads this directly from the Fargate
|
|
36
|
+
// task and MicroVM image. Setting a retention rule makes it sweep OpenCode
|
|
37
|
+
// sessions idle longer than 24h; protected/active sessions are never touched.
|
|
38
|
+
// The 24h window rather than 7d keeps the session database bounded at the growth
|
|
39
|
+
// rate observed in #537. The sweep runs on the default 1h interval.
|
|
40
|
+
exports.SESSION_CLEANUP_MAX_AGE = '24h';
|
|
41
|
+
// The JSON field inside the controller signing secret is shared by the Lambdas
|
|
42
|
+
// that name it and by whatever produces the secret; this is never the secret
|
|
43
|
+
// value. It is customer-facing and separate from persisted doorbell messages
|
|
44
|
+
// and telemetry. Changing the value silently stops an existing adopter's
|
|
45
|
+
// secret from being read.
|
|
46
|
+
exports.CONTROLLER_SIGNING_SECRET_KEY = 'CONTROLLER_SIGNING_SECRET';
|
|
@@ -4,12 +4,10 @@ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
|
|
|
4
4
|
import { type MicrovmShape } from './shapes';
|
|
5
5
|
export interface EvidentMicrovmConstructProps {
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* stays a pure function of its inputs instead of depending on how the
|
|
10
|
-
* ambient checkout was cloned.
|
|
7
|
+
* Directory containing the consumer's Dockerfile and every file it copies,
|
|
8
|
+
* typically the return value of `stageMicrovmBuildContext()`.
|
|
11
9
|
*/
|
|
12
|
-
readonly
|
|
10
|
+
readonly buildContextPath: string;
|
|
13
11
|
/**
|
|
14
12
|
* ARN of the Lambda-managed base MicroVM image, from
|
|
15
13
|
* `aws lambda-microvms list-managed-microvm-images`. A string, not a `CfnParameter`
|
|
@@ -33,14 +31,14 @@ export interface EvidentMicrovmConstructProps {
|
|
|
33
31
|
readonly shapes?: readonly MicrovmShape[];
|
|
34
32
|
/**
|
|
35
33
|
* The secret holding the shared HMAC key the controller verifies every
|
|
36
|
-
* doorbell against, under the field `
|
|
34
|
+
* doorbell against, under the field `CONTROLLER_SIGNING_SECRET`. Any Secrets Manager
|
|
37
35
|
* secret, however the caller manages it — this construct has no opinion on
|
|
38
36
|
* SOPS, KMS, or any other production mechanism. Required (not defaulted
|
|
39
37
|
* here): a default would need a scope of its own, and a caller-owned
|
|
40
38
|
* default keeps its own logical id — the caller builds one exactly once,
|
|
41
39
|
* in its own scope, and passes the result in either case.
|
|
42
40
|
*/
|
|
43
|
-
readonly
|
|
41
|
+
readonly controllerSigningSecret: secretsmanager.ISecret;
|
|
44
42
|
/**
|
|
45
43
|
* Optional runner secret whose JSON values are exported by `/run`. Omitting it
|
|
46
44
|
* leaves GitHub and MCP credentials unavailable to a generic consumer.
|
|
@@ -65,7 +63,7 @@ export interface EvidentMicrovmConstructProps {
|
|
|
65
63
|
* TCP port the image's hook server listens on, baked into both the image
|
|
66
64
|
* (`HOOKS_PORT` env var) and the `MicrovmImage`'s `hooks.port` — a mismatch
|
|
67
65
|
* means AWS probes a port nothing binds. Caller-supplied because the hook
|
|
68
|
-
* server is part of
|
|
66
|
+
* server is part of the base image, which this construct does not build.
|
|
69
67
|
*/
|
|
70
68
|
readonly hooksPort: number;
|
|
71
69
|
/**
|
|
@@ -37,17 +37,16 @@ exports.EvidentMicrovmConstruct = void 0;
|
|
|
37
37
|
const path = __importStar(require("path"));
|
|
38
38
|
const cdk = __importStar(require("aws-cdk-lib"));
|
|
39
39
|
const constructs_1 = require("constructs");
|
|
40
|
+
const iam = __importStar(require("aws-cdk-lib/aws-iam"));
|
|
40
41
|
const lambda = __importStar(require("aws-cdk-lib/aws-lambda"));
|
|
41
42
|
const s3 = __importStar(require("aws-cdk-lib/aws-s3"));
|
|
42
43
|
const lambda_microvm_cdk_1 = require("@evident-ai/lambda-microvm-cdk");
|
|
44
|
+
const constants_1 = require("./constants");
|
|
43
45
|
const shapes_1 = require("./shapes");
|
|
44
46
|
const construct_1 = require("./image-version-reporter/construct");
|
|
45
47
|
// Image hooks build the snapshot, so they get minutes where the runtime hooks
|
|
46
48
|
// get AWS's 60 s.
|
|
47
49
|
const IMAGE_HOOK_TIMEOUT_SECONDS = 600;
|
|
48
|
-
// The field inside the doorbell secret, shared by this construct (which
|
|
49
|
-
// names it for the controller) and whatever produced `props.doorbellSecret`.
|
|
50
|
-
const DOORBELL_SECRET_KEY = 'DOORBELL_SECRET';
|
|
51
50
|
/**
|
|
52
51
|
* The customer-account half of the per-session runner (#558): the MicroVM
|
|
53
52
|
* image and the roles it builds and runs as, plus the stateless controller
|
|
@@ -127,10 +126,8 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
|
|
|
127
126
|
// ("Environment variable key 'AWS_REGION' is reserved", 400), which is
|
|
128
127
|
// also why the synchroniser's S3 client does not need it set — the
|
|
129
128
|
// MicroVM runtime supplies the region itself.
|
|
130
|
-
// Read by `evident run` itself (not the hooks), the same 24h window ECS
|
|
131
|
-
// already bakes in for its own runners (evident-scale-to-zero-construct.ts:191).
|
|
132
129
|
// Safe to bake: identical for every VM from this image version.
|
|
133
|
-
EVIDENT_SESSION_CLEANUP_MAX_AGE:
|
|
130
|
+
EVIDENT_SESSION_CLEANUP_MAX_AGE: constants_1.SESSION_CLEANUP_MAX_AGE,
|
|
134
131
|
...(props.runnerOpencodeConfigPath
|
|
135
132
|
? { RUNNER_OPENCODE_CONFIG: props.runnerOpencodeConfigPath }
|
|
136
133
|
: {}),
|
|
@@ -149,42 +146,31 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
|
|
|
149
146
|
lifecycleTimeout: cdk.Duration.seconds(props.hookTimeoutSeconds),
|
|
150
147
|
buildTimeout: cdk.Duration.seconds(IMAGE_HOOK_TIMEOUT_SECONDS),
|
|
151
148
|
};
|
|
152
|
-
// Built first
|
|
153
|
-
//
|
|
154
|
-
// shape
|
|
155
|
-
|
|
156
|
-
// and current logical id — zero CloudFormation churn on the live,
|
|
157
|
-
// hand-deployed stack (D3/D9). Every additional shape below borrows this
|
|
158
|
-
// role rather than getting one of its own, which is why `validateShapes`
|
|
159
|
-
// requires the default shape to be `shapes[0]`: it must exist before any
|
|
160
|
-
// other shape can reference its execution role.
|
|
161
|
-
const defaultImage = new lambda_microvm_cdk_1.MicrovmImage(this, defaultShape.constructId, {
|
|
149
|
+
// Built first without an `executionRole` prop, so every additional shape
|
|
150
|
+
// can borrow this image's execution role. `validateShapes` requires the
|
|
151
|
+
// default shape to be `shapes[0]` because that role must exist first.
|
|
152
|
+
const defaultImage = new lambda_microvm_cdk_1.MicrovmImage(this, defaultShape.name, {
|
|
162
153
|
imageName: defaultShape.imageName,
|
|
163
154
|
description: defaultShape.description,
|
|
164
155
|
// A local directory, so the zip is uploaded by `cdk deploy` itself and the
|
|
165
156
|
// image ARN is a stack reference rather than a deploy-time input.
|
|
166
|
-
source: props.
|
|
157
|
+
source: props.buildContextPath,
|
|
167
158
|
baseImageArn: props.baseImageArn,
|
|
168
159
|
baseImageVersion: props.baseImageVersion,
|
|
169
160
|
memoryMiB: defaultShape.memoryMiB,
|
|
170
161
|
environment: imageEnvironment,
|
|
171
162
|
hooks: imageHooks,
|
|
172
163
|
});
|
|
173
|
-
// WART
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
// intentional — see D3 in the plan. Do NOT "fix" this with
|
|
178
|
-
// `overrideLogicalId`: renaming the logical id IS the CloudFormation
|
|
179
|
-
// churn this design exists to avoid (a delete-and-recreate of the role a
|
|
180
|
-
// running MicroVM has live session credentials from). The wart is one
|
|
181
|
-
// comment; the override would undo the point of D3.
|
|
164
|
+
// WART: every additional shape shares `defaultImage.executionRole`, so the
|
|
165
|
+
// role's path under the default image reads as if it belongs to one shape
|
|
166
|
+
// even though it serves all of them. Keeping one role avoids replacing a
|
|
167
|
+
// role that running MicroVMs may still have credentials from.
|
|
182
168
|
const images = new Map([[defaultShape.name, defaultImage]]);
|
|
183
169
|
for (const shape of additionalShapes) {
|
|
184
|
-
images.set(shape.name, new lambda_microvm_cdk_1.MicrovmImage(this, shape.
|
|
170
|
+
images.set(shape.name, new lambda_microvm_cdk_1.MicrovmImage(this, shape.name, {
|
|
185
171
|
imageName: shape.imageName,
|
|
186
172
|
description: shape.description,
|
|
187
|
-
source: props.
|
|
173
|
+
source: props.buildContextPath,
|
|
188
174
|
baseImageArn: props.baseImageArn,
|
|
189
175
|
baseImageVersion: props.baseImageVersion,
|
|
190
176
|
memoryMiB: shape.memoryMiB,
|
|
@@ -193,13 +179,14 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
|
|
|
193
179
|
hooks: imageHooks,
|
|
194
180
|
}));
|
|
195
181
|
}
|
|
196
|
-
//
|
|
197
|
-
// the
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
182
|
+
// The execution role serves every image shape, so per-session state is scoped
|
|
183
|
+
// to the `runners/` root; vault credentials need only GetObject and PutObject.
|
|
184
|
+
durableState.grantReadWrite(defaultImage.executionRole, 'runners/*');
|
|
185
|
+
defaultImage.executionRole.addToPrincipalPolicy(new iam.PolicyStatement({
|
|
186
|
+
sid: 'ReadWriteCredentialVaultObjects',
|
|
187
|
+
actions: ['s3:GetObject', 's3:PutObject'],
|
|
188
|
+
resources: [durableState.arnForObjects('credential-vaults/*')],
|
|
189
|
+
}));
|
|
203
190
|
// The image receives only an ARN; `/run` reads the secret value at runtime.
|
|
204
191
|
props.runnerSecret?.grantRead(defaultImage.executionRole);
|
|
205
192
|
// The shape catalogue the controller resolves a doorbell's `shape` field
|
|
@@ -239,8 +226,8 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
|
|
|
239
226
|
// The ARN and field name, never the value: the controller reads it from
|
|
240
227
|
// Secrets Manager at runtime, so the key is not exposed to anyone with
|
|
241
228
|
// lambda:GetFunctionConfiguration.
|
|
242
|
-
|
|
243
|
-
|
|
229
|
+
CONTROLLER_SIGNING_SECRET_ARN: props.controllerSigningSecret.secretArn,
|
|
230
|
+
CONTROLLER_SIGNING_SECRET_KEY: constants_1.CONTROLLER_SIGNING_SECRET_KEY,
|
|
244
231
|
MICROVM_SHAPES: shapeCatalogueJson,
|
|
245
232
|
// The role every shape's MicroVM shares (D3) — still one role, still
|
|
246
233
|
// this same expression, now documented as shared rather than singular.
|
|
@@ -255,7 +242,7 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
|
|
|
255
242
|
// emit N near-identical policy statements and break the "exactly one
|
|
256
243
|
// statement granting X" guarantees this construct's test encodes.
|
|
257
244
|
defaultImage.grantRun(controller);
|
|
258
|
-
props.
|
|
245
|
+
props.controllerSigningSecret.grantRead(controller);
|
|
259
246
|
const functionUrl = controller.addFunctionUrl({
|
|
260
247
|
// HMAC verification of the doorbell body IS the auth, as for the ECS waker.
|
|
261
248
|
authType: lambda.FunctionUrlAuthType.NONE,
|
|
@@ -265,7 +252,7 @@ class EvidentMicrovmConstruct extends constructs_1.Construct {
|
|
|
265
252
|
// no custom resource — this branch must not run for them.
|
|
266
253
|
if (props.evidentApiUrl && props.evidentRunnerId) {
|
|
267
254
|
new construct_1.ImageVersionReporter(this, 'ImageVersionReporter', {
|
|
268
|
-
|
|
255
|
+
controllerSigningSecret: props.controllerSigningSecret,
|
|
269
256
|
evidentApiUrl: props.evidentApiUrl,
|
|
270
257
|
evidentRunnerId: props.evidentRunnerId,
|
|
271
258
|
imageVersions: Object.fromEntries(shapes.map((shape) => [shape.name, images.get(shape.name).latestActiveImageVersion])),
|
|
@@ -27,12 +27,12 @@ declare const WAKE_EVENT_TYPES: readonly ["agent.message_queued", "agent.wake_re
|
|
|
27
27
|
declare const SUSPEND_EVENT_TYPE = "runner.suspend_requested";
|
|
28
28
|
export declare const SHAPES_EVENT_TYPE = "runner.shapes_requested";
|
|
29
29
|
type WakeEventType = (typeof WAKE_EVENT_TYPES)[number];
|
|
30
|
-
type
|
|
30
|
+
type DoorbellRequestFields = {
|
|
31
31
|
runnerId: string;
|
|
32
32
|
occurredAt: string;
|
|
33
33
|
microvmId?: string;
|
|
34
34
|
};
|
|
35
|
-
export type
|
|
35
|
+
export type WakeDoorbellRequest = DoorbellRequestFields & {
|
|
36
36
|
type: WakeEventType;
|
|
37
37
|
runHookPayload: string;
|
|
38
38
|
/**
|
|
@@ -46,7 +46,7 @@ export type WakeDoorbell = DoorbellFields & {
|
|
|
46
46
|
*/
|
|
47
47
|
recreateOnOutdatedImage?: boolean;
|
|
48
48
|
};
|
|
49
|
-
export type
|
|
49
|
+
export type DoorbellRequest = WakeDoorbellRequest | (DoorbellRequestFields & {
|
|
50
50
|
type: typeof SUSPEND_EVENT_TYPE;
|
|
51
51
|
}) | {
|
|
52
52
|
type: typeof SHAPES_EVENT_TYPE;
|
|
@@ -67,12 +67,12 @@ export declare const DOORBELL_REJECTION: {
|
|
|
67
67
|
readonly runPayloadTooLarge: "run_payload_too_large";
|
|
68
68
|
};
|
|
69
69
|
export type DoorbellRejection = (typeof DOORBELL_REJECTION)[keyof typeof DOORBELL_REJECTION];
|
|
70
|
-
export type
|
|
70
|
+
export type ParsedDoorbellRequest = {
|
|
71
71
|
ok: true;
|
|
72
|
-
doorbell:
|
|
72
|
+
doorbell: DoorbellRequest;
|
|
73
73
|
} | {
|
|
74
74
|
ok: false;
|
|
75
75
|
reason: DoorbellRejection;
|
|
76
76
|
};
|
|
77
|
-
export declare function
|
|
77
|
+
export declare function parseDoorbellRequest(rawBody: string): ParsedDoorbellRequest;
|
|
78
78
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DOORBELL_REJECTION = exports.SHAPES_EVENT_TYPE = void 0;
|
|
4
|
-
exports.
|
|
4
|
+
exports.parseDoorbellRequest = parseDoorbellRequest;
|
|
5
5
|
const constants_1 = require("../constants");
|
|
6
6
|
/**
|
|
7
7
|
* The doorbell Evident POSTs to the controller's Function URL. `run_payload` is
|
|
@@ -52,7 +52,7 @@ function nonBlankString(value) {
|
|
|
52
52
|
function isWakeType(value) {
|
|
53
53
|
return WAKE_EVENT_TYPES.includes(value);
|
|
54
54
|
}
|
|
55
|
-
function
|
|
55
|
+
function parseDoorbellRequest(rawBody) {
|
|
56
56
|
let body;
|
|
57
57
|
try {
|
|
58
58
|
body = JSON.parse(rawBody);
|
|
@@ -17,10 +17,10 @@ export type DoorbellResult = {
|
|
|
17
17
|
kind: ActionKind;
|
|
18
18
|
};
|
|
19
19
|
};
|
|
20
|
-
export declare function handleDoorbell({ rawBody, signatureHeader,
|
|
20
|
+
export declare function handleDoorbell({ rawBody, signatureHeader, controllerSigningSecret, shapes, microvm, sleep, random, }: {
|
|
21
21
|
rawBody: string;
|
|
22
22
|
signatureHeader: string | undefined;
|
|
23
|
-
|
|
23
|
+
controllerSigningSecret: string;
|
|
24
24
|
shapes: ShapeCatalogue;
|
|
25
25
|
microvm: MicrovmClient;
|
|
26
26
|
} & Timing): Promise<DoorbellResult>;
|