Relay Implementation

Full implementation plan for turning the stub Lambda handler into a working configurable webform relay.

What it does

Webform Relay accepts HTTP POST submissions at /api/v1/submit/{form-name}, maps the input fields according to a YAML config, and fans out the result to one or more output targets — all without redeployment when the config changes.

Architecture

Browser
  │  POST /api/v1/submit/{form-name}
  ▼
API Gateway
  │
  ▼
Lambda (Go container)
  ├── Load config from S3 (TTL cache, default 30s)
  ├── Route to named form
  ├── Validate input fields
  ├── Apply field mapping
  └── Fan-out to output targets
        ├── HTTP POST  (GitLab issue form, any webhook)
        ├── Email      (Amazon SES)
        └── Salesforce Web2Lead

Config

Config is a YAML file stored in S3. The Lambda reads CONFIG_BUCKET and CONFIG_KEY from environment variables, caches the parsed config in memory for CACHE_TTL seconds, and re-fetches on cache expiry. No container rebuild or redeployment is needed to change config — update the S3 file and wait for the TTL.

cache_ttl: 30s

forms:
  contact:
    fields:
      - name: name
        required: true
      - name: email
        required: true
        type: email
      - name: message
        required: true
        type: textarea
    outputs:
      - type: email
        to: hello@example.com
        subject: "New contact: {{.name}}"
        mapping:
          from_name: name
          body: message
        static:
          source: website
      - type: http_post
        url: https://gitlab.example.com/api/v4/projects/1/issues
        headers:
          Authorization: "Bearer {{env.GITLAB_TOKEN}}"
        mapping:
          title: name
          description: message

  newsletter:
    fields:
      - name: email
        required: true
    outputs:
      - type: salesforce_web2lead
        url: https://webto.salesforce.com/servlet/servlet.WebToLead
        mapping:
          email: email
        static:
          oid: "00Dxxxxxxxxxxxxxxx"
          retURL: https://example.com/thanks

Output targets

TypeWhat it does
http_postPosts mapped fields as application/x-www-form-urlencoded to any URL
emailSends via Amazon SES
salesforce_web2leadPosts to the Salesforce Web-to-Lead endpoint

Multiple outputs per form are all attempted on every submission (fan-out).

CLI

A webform-relay binary is built by task build:cli. It reads a local config file (not S3) and is used for development and CI.

webform-relay form html contact     # print the HTML form to stdout
webform-relay form list             # list all configured form names
webform-relay config validate       # validate the config file

Development process

Each internal package is built with a strict Red-Green cycle:

  1. Opus writes failing tests that define the interface and behaviour.
  2. Sonnet implements until the tests are green.
  3. Haiku runs task test:all and confirms.
  4. Opus reviews the implementation assuming it is wrong and lists issues.
  5. Sonnet fixes every issue.
  6. Haiku confirms tests are still green.

Components are completed in this order:

  1. internal/config — structs, YAML parsing, S3 loader, TTL cache
  2. internal/form — validation, HTML renderer
  3. internal/mapping — field rename + static value injection
  4. internal/relay/http — HTTP POST target
  5. internal/relay/email — SES email target
  6. internal/relay/salesforce — Salesforce Web2Lead target
  7. internal/relay — fan-out coordinator
  8. internal/handler — Lambda handler + routing
  9. cmd/cli — CLI binary