Skip to content

metascriptlang/lambda-runtime

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@metascript/lambda-runtime

AWS Lambda Custom Runtime for MetaScript. Compiles to a native bootstrap binary that plugs straight into Lambda's provided.al2 / provided.al2023 runtimes.

Status: v0.1 — usable for experiments and internal projects. Primary real user: Metacraft Studio. Not production-commercial-stable.

Why

  • Small cold start. No JIT, no VM warm-up — the binary is what runs.
  • Small binaries. A plain-HTTP handler compiles to ~1 MB; HTTPS (via fetch or std/https) adds mbedtls (~4 MB total).
  • Typed events. parseApiGwV2 gives you a struct, not a string.
  • Result-first errors. Handler returns Result<string, LambdaError>; failures auto-format to AWS's /error shape.

Quickstart

Write the handler:

import {
  startLambda, LambdaContext, LambdaError, getTimeRemaining,
} from "@metascript/lambda-runtime";
import {
  parseApiGwV2, serializeApiGwV2Response, ApiGwV2Response,
} from "@metascript/lambda-runtime/events";
import { createHeaders, setHeader } from "std/http";

function handler(event: string, ctx: LambdaContext): Result<string, LambdaError> {
  const reqR = parseApiGwV2(event);
  if (!reqR.ok) {
    return Result.err({ errorType: "BadEvent", errorMessage: reqR.error });
  }
  const req = reqR.value;

  const headers = createHeaders();
  setHeader(headers, "content-type", "application/json");

  const resp: ApiGwV2Response = {
    statusCode: 200,
    headers: headers,
    body: `{"method":"${req.method}","path":"${req.path}","timeLeft":${getTimeRemaining(ctx)}}`,
  };
  return Result.ok(serializeApiGwV2Response(resp));
}

startLambda(handler);

Build + package:

# Linux arm64 (Graviton) — cheapest + fastest cold start on Lambda
msc build handler.ms --target=c --os=linux --cpu=arm64 --release --strip --output=bootstrap
chmod +x bootstrap
zip deploy.zip bootstrap

Deploy (AWS CLI):

aws lambda create-function \
  --function-name metascript-hello \
  --runtime provided.al2023 \
  --architectures arm64 \
  --handler bootstrap \
  --role arn:aws:iam::<account>:role/lambda-basic-exec \
  --zip-file fileb://deploy.zip

Wire an HTTP API to invoke it, or call directly:

aws lambda invoke --function-name metascript-hello \
  --payload '{"version":"2.0","rawPath":"/hi","headers":{},"body":"","isBase64Encoded":false,"requestContext":{"requestId":"r-1","http":{"method":"GET","path":"/hi"}}}' \
  out.json
cat out.json

API

Core invocation

Symbol Purpose
startLambda(handler) Start the runtime loop. Blocks.
LambdaContext Per-invocation fields: requestId, deadline, traceId, function info, etc.
LambdaError { errorType, errorMessage } — serialized to AWS /error format.
LambdaHandler (event: string, ctx: LambdaContext) → Result<string, LambdaError>.
getTimeRemaining(ctx) int64 ms until deadline. Negative means overdue. Mirrors AWS SDK.

Events (@metascript/lambda-runtime/events)

Symbol Purpose
ApiGwV2Event Parsed HTTP API / Function URL request: method, path, headers, body, requestId.
ApiGwV2Response Response builder: statusCode, headers, body.
parseApiGwV2(json) Result<ApiGwV2Event, string>.
serializeApiGwV2Response(resp) JSON string ready for /response POST.

Event types in scope for v0.1: APIGw v2 / Function URL only. SQS, S3, EventBridge, APIGw v1 deferred — hand-parse via std/core/json in the meantime, or open an issue if you hit one.

Runtime behavior

  • Invocation loop: GET /invocation/next (blocks), dispatch to handler, POST /response or /error, repeat.
  • Bounded retry: 3 consecutive transport failures → process exits, Lambda recycles the container. Counter resets after a successful invocation cycle. Mirrors aws-lambda-cpp.
  • X-Ray trace propagation: _X_AMZN_TRACE_ID env var is set per invocation from the Lambda-Runtime-Trace-Id header so AWS SDK child calls inherit the trace context.
  • Handler errors don't kill the loop: Result.err goes to /error, runtime moves on to the next invocation.

Known gaps (v0.1)

  • No /init/error on cold-start crash — top-level MS exceptions don't yet auto-report to Lambda.
  • No persistent HTTP client / connection pool. Runtime API calls open a new TCP connection each invocation. Measured <0.2 ms/req on localhost — not a bottleneck today, but a candidate for v0.2 if real workloads argue for it.
  • Event types beyond APIGw v2 (SQS, S3, EventBridge) not yet typed — hand-parse via std/core/json.
  • No SnapStart support (runtime concern).

File layout

lambda-runtime/
├── index.cms           barrel — public API
├── types.cms           LambdaContext, LambdaError, LambdaHandler, getTimeRemaining
├── runtime.cms         startLambda loop (bounded retry + X-Ray + POST /error)
├── environment.cms     readConfig — AWS_LAMBDA_* env vars at cold start
├── events/
│   ├── index.cms       barrel
│   └── apigw-v2.cms    parseApiGwV2 + serializeApiGwV2Response
└── examples/
    ├── hello/          raw-string handler
    └── apigw-hello/    typed APIGw v2 handler + deploy recipe

Inline tests live next to the code they cover — run with:

msc test lambda-runtime/events/apigw-v2.cms

See also

  • examples/apigw-hello/deploy.md — step-by-step AWS deploy walkthrough
  • CLAUDE.md — design notes, reference alignment (aws-lambda-cpp), open questions
  • https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html — AWS Runtime API spec

About

AWS Lambda runtime for MetaScript

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages