Skip to content

Shared Lambda Interface

Plan Metadata

  • Plan type: plan
  • Parent plan: N/A
  • Depends on: N/A
  • Status: documentation

System Intent

  • What is being built: Shared API lambda invoker that can be used to apply auth and standardize input and output and simplify core functionality.
  • Primary consumer(s): Python API lambda handlers.
  • Boundary (black-box scope only): Input event parsing, auth header/token handling, handler invocation contract, and normalized response envelopes including preflight and error behavior.

Stage Gate Tracker

  • [x] Stage 1 Mermaid approved
  • [x] Stage 2 I/O contracts approved
  • [x] Stage 3 pseudocode approved or skipped

1. Mermaid Diagram

Reference: .agent/skills/create-mermaid-diagram/SKILL.md

flowchart TD
  A[lambda_handler] -->|event context settings handler| B[invoke_lambda - layers/shared/python/shared/lambda_helpers.py]
  B -->|optional CORS preflight path on OPTIONS| C[maybe_handle_cors_preflight - layers/shared/python/shared/lambda_helpers.py]
  C -->|continue request lifecycle| D[maybe_require_auth_context - layers/shared/python/shared/lambda_helpers.py]
  D -->|parse request payload| E[parse_json_event_body - layers/shared/python/shared/lambda_helpers.py]
  E -->|payload and optional auth| F[handler - layers/shared/python/shared/lambda_helpers.py]
  F -->|return normalized success payload| G[build_standard_response - layers/shared/python/shared/lambda_helpers.py]
  D -->|auth validation failure| H[build_error_response - layers/shared/python/shared/lambda_helpers.py]
  F -->|application or unexpected error| H

  classDef unchanged fill:#d3d3d3,stroke:#666,stroke-width:1px;
  class A,B,C,D,E,F,G,H unchanged;

2. Black-Box Inputs and Outputs

Global Types

Define shared types used in the invoke_lambda integration contract.

LambdaInvokeInput {
  event: map<string, any> (API Gateway-style event; body may be object, JSON string, or omitted)
  context: any (lambda context)
  settings: LambdaSettings
  handler: function(payload, auth?) -> object
  require_auth_context: boolean (default true)
}

LambdaSettings {
  lambda_name: string (required)
}

AuthContext {
  user_id: string (required; Cognito subject)
  token_use: string (expected access)
  claims: map<string, any>
}

SuccessEnvelope {
  statusCode: 200
  headers: map<string, string>
  body: {
    success: true
    data: object
  }
}

TypedErrorEnvelope {
  statusCode: number
  headers: map<string, string>
  body: {
    success: false
    code: string
    message: string
  }
}

GenericErrorEnvelope {
  statusCode: 500
  headers: map<string, string>
  body: {
    success: false
    message: string
  }
}

PreflightResponse {
  statusCode: 200
  headers: map<string, string>
  body: ""
}

Flow: main/server/layers/shared/python/shared/lambda_helpers.py.invoke_lambda, main/server/tests/unit/test_invoke_lambda.py, main/server/tests/unit/test_auth_helpers.py

path-name input output/expected state change path-type notes updated
invoke-lambda.integration.preflight-options LambdaInvokeInput with http_method=OPTIONS PreflightResponse subpath returns CORS headers immediately; handler is not executed
invoke-lambda.integration.success-body-dict require_auth_context=false; event.body is object SuccessEnvelope happy path payload object is passed to handler
invoke-lambda.integration.success-body-json-string require_auth_context=false; event.body is JSON string SuccessEnvelope subpath body string is JSON-decoded before handler call
invoke-lambda.integration.success-body-unsupported-type require_auth_context=false; event.body is non-string and non-object SuccessEnvelope subpath payload is normalized to empty object
invoke-lambda.integration.success-event-root require_auth_context=false; event.body missing SuccessEnvelope subpath root event object is used as payload
invoke-lambda.integration.success-single-arg-handler handler signature is function(payload) SuccessEnvelope subpath runtime calls handler without auth arg when signature has one positional arg
invoke-lambda.integration.success-auth-required require_auth_context=true; valid bearer access token SuccessEnvelope happy path handler receives resolved AuthContext
invoke-lambda.integration.error-invalid-json-body event.body is invalid JSON string GenericErrorEnvelope statusCode=500 message=<json parse error> error parse failure is treated as unexpected exception
invoke-lambda.integration.error-auth-missing-header require_auth_context=true; Authorization missing TypedErrorEnvelope statusCode=401 code=AUTH_MISSING error handler is not executed
invoke-lambda.integration.error-auth-invalid-header require_auth_context=true; Authorization not Bearer <token> TypedErrorEnvelope statusCode=401 code=AUTH_INVALID error malformed auth header
invoke-lambda.integration.error-auth-config-missing require_auth_context=true; missing AWS_REGION or COGNITO_USER_POOL_ID or COGNITO_APP_CLIENT_ID TypedErrorEnvelope statusCode=401 code=AUTH_CONFIG_MISSING error auth settings are required before JWT verification
invoke-lambda.integration.error-auth-key-missing require_auth_context=true; token kid not found in JWKS TypedErrorEnvelope statusCode=401 code=AUTH_KEY_MISSING error token key lookup fails during JWT verification
invoke-lambda.integration.error-auth-claims-invalid require_auth_context=true; token_use/client_id/sub invalid TypedErrorEnvelope statusCode=401 code=AUTH_INVALID error invalid token claims are rejected
invoke-lambda.integration.error-auth-jwks-fetch-failure require_auth_context=true; JWKS endpoint/network failure GenericErrorEnvelope statusCode=500 message=<fetch error> error non-AppError auth dependency failures bubble as generic runtime errors
invoke-lambda.integration.error-handler-app-error handler raises AppError/AuthError TypedErrorEnvelope status/code/message from exception error preserves typed application errors
invoke-lambda.integration.error-handler-unexpected handler raises Exception GenericErrorEnvelope statusCode=500 message=<exception> error unexpected exceptions map to generic 500 envelope
invoke-lambda.integration.error-missing-lambda-name settings.lambda_name missing ValueError is raised before envelope response error configuration failure at wrapper entry

3. Pseudocode for Critical Flows

  • Flow name:: invoke_lambda request lifecycle
    read settings.lambda_name and fail fast if missing
    derive request metadata for logging
    if http_method == OPTIONS then return 200 with CORS headers and empty body
    parse payload from event body or root event
    if require_auth_context true then resolve auth context from bearer token
    inspect handler signature to decide one-arg or two-arg call
    invoke handler and return success envelope
    if AppError or AuthError occurs then return typed error envelope
    if any other exception occurs then return generic 500 envelope
    
  • Flow name:: maybe_handle_cors_preflight
    read LOCAL_CORS_ORIGIN env value
    if http_method != OPTIONS then continue request lifecycle
    if http_method == OPTIONS then return {
      statusCode: 200
      headers: {
        Access-Control-Allow-Origin: LOCAL_CORS_ORIGIN
        Access-Control-Allow-Methods: DELETE,GET,OPTIONS,POST,PUT
        Access-Control-Allow-Headers: Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token,X-Encache-Client
      }
      body: ""
    }
    
  • Flow name:: maybe_require_auth_context
    if require_auth_context is false then return auth_context=None
    read Authorization header (case-insensitive)
    require format "Bearer <jwt>" else raise AuthError AUTH_MISSING or AUTH_INVALID
    require env AWS_REGION, COGNITO_USER_POOL_ID, COGNITO_APP_CLIENT_ID else raise AUTH_CONFIG_MISSING
    fetch JWKS, select key by kid, verify JWT signature and issuer
    require token_use=access, client_id matches app client id, and sub exists
    return AuthContext { user_id=sub, token_use, claims }
    
  • Flow name:: auth error mapping
    if auth function raises AuthError then invoke_lambda returns TypedErrorEnvelope (401 with error code and message)
    if jwks fetch or other non-AppError auth dependency fails then invoke_lambda returns GenericErrorEnvelope (500)
    
  • Implementation notes: This plan documents existing behavior in lambda_helpers.py; downstream API plans should treat these envelopes and auth semantics as shared contracts.

After all stages are approved, apply .agent/skills/reconcile-plans/SKILL.md to propagate contract updates across linked plans.