A minimal-but-realistic AWS sample that exercises every piece of the PulseBoard observability stack on serverless:
| PulseBoard tool | Where it shows up here |
|---|---|
pulse-lambda-extension |
Layer attached to both functions. Pushes cold-start, duration, billed-duration and memory metrics straight to PulseBoard — no CloudWatch hop. |
@open-pulseboard/tracer |
Bundled in the Node API function. Auto-instruments http, @aws-sdk/client-sqs, etc. |
pulseboard-tracer (Python) |
Bundled in the Python worker function. Auto-instruments boto3, botocore, etc. |
W3C traceparent propagation |
API → SQS message attribute → Worker → DynamoDB span — all stitched into one distributed trace. |
client
│
│ POST /orders {sku, qty}
▼
┌──────────────┐ enqueue (traceparent in attrs) ┌──────────────┐
│ API GW v2 │ ───────────────────────────────────► │ SQS queue │
└──────────────┘ └──────┬───────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ ApiFunction │ │ WorkerFn │
│ Node 22 + │ │ Python 3.12 +│
│ @pulseboard/ │ │ pulseboard- │
│ tracer │ │ tracer │
│ + extension │ │ + extension │
└──────────────┘ └──────┬───────┘
│ │
│ all spans + extension metrics ▼
│ ┌──────────────┐
└───────────────────► PulseBoard ◄────────── │ DynamoDB │
workspace │ OrdersTable │
└──────────────┘
See docs/ARCHITECTURE.md for a fuller walk-through.
- AWS account + credentials (
aws sts get-caller-identityworks). - AWS SAM CLI ≥ 1.110.
- Docker (SAM builds the Python function in a container).
- Rust +
x86_64-unknown-linux-musltarget, OR a pre-builtpulse-extensionbinary, to assemble the extension layer. - A PulseBoard workspace URL (
PULSE_URL) and ingest key (PULSE_API_KEY).
# 1. Set the two secrets (consumed by build-layer.sh + sam deploy).
export PULSE_URL=https://<your-workspace>.pulseboard.cloud
export PULSE_API_KEY=sk_...
# 2. Point the layer builder at your local pulseboard-extensions checkout
# (default: ../pulseboard-extensions).
export PULSE_EXTENSIONS_DIR=../pulseboard-extensions
# 3. Build the extension layer, then the functions.
make layer
make build
# 4. First deploy is guided — pick a stack name, region, accept defaults.
make deploy.guided
# Subsequent deploys: make deploy
# 5. Hit the API a few times.
make smoke
# 6. Open your PulseBoard workspace. Within ~30s:
# - Service catalog shows pulseboard-aws-sample-api + -worker
# - Traces tab shows one trace per /orders POST spanning both functions
# - Metrics: aws_lambda_init_duration_ms, duration_ms, billed_duration_ms
# 7. Tear it down.
make destroypulseboard-aws-sample/
├── template.yaml SAM template (API GW + 2 fns + SQS + DDB + layer)
├── samconfig.toml.example Copy to samconfig.toml + edit
├── Makefile layer / build / deploy / smoke / destroy
├── functions/
│ ├── api/ Node 22 — API GW entry, enqueues to SQS
│ └── worker/ Python 3.12 — SQS-triggered, writes DynamoDB
├── scripts/
│ ├── build-layer.sh Cross-compiles pulse-extension → layer zip
│ ├── publish-layer.sh (optional) publish to your account as a re-usable layer
│ └── smoke.sh curl POST /orders × 5
├── layers/ Build artefacts; gitignored
└── docs/
├── ARCHITECTURE.md
└── DEPLOYMENT.md
The sample stays comfortably inside AWS free tier on a quiet account:
2 Lambda functions, 1 SQS queue, 1 on-demand DynamoDB table, 1 HTTP API.
Run make destroy when you're done.
MIT — see LICENSE.