Infrastructure
All infrastructure is AWS, defined in Terraform under deploy/ and applied per target. Resource names carry the site prefix (test, staging, or prod, e.g. prod-txn-api); the application identifier is txn. There are four targets: the old test environment in the shared account (us-west-2), the new test environment in its own account (us-east-2), staging in its own account (us-east-2), and prod in the shared account (us-east-2). The legacy tables live in us-east-1 for new test, staging, and prod and in the deploy region for old test; the legacy_region variable selects it.
Deployment
The workflows are split by trigger: on_pr.yaml plans every continuous target, on_main.yaml applies them, and on_release.yaml applies prod. Each target is one call of the reusable deploy-env.yaml, which takes the account, deploy region, legacy region, state bucket, and artifact bucket as inputs and fetches the tarball built once earlier in the run (or attached to the published release for prod). The Lambda packages are uploaded to the target’s artifact bucket (artifact_bucket variable): floatme-test-media for old test, floatme-test-services-artifacts for new test, floatme-staging-media for staging, and floatme-prod-media for prod. New test also passes two overrides its account needs: bin_list_bucket_name = floatme-test-txn-bin-list-data (the default test-txn-bin-list-data is old test’s bucket) and auth0_event_bus_name = test-auth0 (the account bridges the Auth0 partner source onto its own bus; with the override set, the partner-source lookup is skipped).
The workflow authenticates to AWS with GitHub OIDC; there are no static credentials. Pull requests assume the read-only site-github-services-plan role and run terraform plan; pushes to main and published releases assume site-github-services-deploy and run terraform apply. Both wait up to five minutes for the state lock instead of failing when another run holds it. Both roles, and the site-github-services-boundary permissions boundary carried by every Lambda role and by the site-txn-eventbridge-auth0 rule role, are owned by the infrastructure repo. The Terraform providers use the workflow’s credentials directly, with no further role assumption. State lives in the target’s state bucket (floatme-test-media, floatme-test-services-terraform-state, floatme-staging-services-terraform-state, or floatme-prod-media) under terraform/transactions-service/, locked by the terraform-locking table. The provider lock file deploy/.terraform.lock.hcl is committed; provider upgrades go through it.
Lambda Functions
| Function | Trigger | Timeout | Memory | Key IAM Permissions |
|---|---|---|---|---|
|
API Gateway |
300s |
1024 MB |
DynamoDB R/W transactions, plaid, |
|
API Gateway |
300s |
default |
SQS send plaid-webhooks; secret plaid |
|
SQS |
810s |
default |
DynamoDB R/W transactions, plaid, |
|
SQS |
810s |
256 MB |
DynamoDB R/W plaid; Kinesis PutRecords plaid-transactions; SQS receive and send plaid-throttle; invoke User, LOC; secrets plaid, growthbook, segment |
|
Kinesis |
840s |
512 MB |
DynamoDB R/W transactions; Kinesis read; SQS send |
|
DynamoDB Streams (transactions, plaid) |
840s |
default |
DynamoDB Streams read; DynamoDB R/W plaid; Kinesis PutRecords floatme-transactions; EventBridge PutEvents; invoke User; secret plaid |
|
SQS |
840s |
512 MB |
DynamoDB Query transactions, plaid; SQS receive listener-eventbridge, send plaid-webhooks |
|
EventBridge rule |
300s |
256 MB |
DynamoDB PutItem institutions; secret plaid |
The miner and throttle-miner timeouts are 90% of their queue’s 900s visibility timeout.
Event Source Mappings
| Mapping | Configuration |
|---|---|
miner ← |
Batch 10, batching window 2s, |
throttle-miner ← |
Batch 10, |
listener ← |
Batch 10 |
refiner ← |
Batch 10, parallelization factor 2, |
feeder ← |
|
feeder ← |
|
API Gateways
| Gateway | Auth | Routing |
|---|---|---|
|
AWS IAM (SigV4) |
|
|
Plaid webhook signature, verified in the Lambda |
|
site-txn is also reachable as txn.site.svc.floatme.io (deploy/domain.tf): an alias record in the infrastructure repo’s per-environment site.svc.floatme.io zone, using its wildcard certificate. The execute-api endpoint stays enabled for existing callers.
DynamoDB Tables
All tables are Terraform data sources here; the three prefixed tables are provisioned in the infrastructure repo. Schemas and access patterns are in DynamoDB Tables.
| Table | Region | Streams | Used by |
|---|---|---|---|
|
deploy region |
Yes (feeder) |
api, miner, refiner, listener |
|
deploy region |
Yes (feeder) |
api, miner, throttle-miner, feeder |
|
deploy region |
No |
api (read), institution (write) |
|
us-east-1 in prod |
No |
api, miner (written on item removal and main-account change; read by the change check) |
S3
The API Lambda loads the BIN reference list from site-txn-bin-list-data (provisioned in the infrastructure repo) at startup and serves it from memory.
| Setting | Value |
|---|---|
Env vars |
|
Objects |
|
Response cap |
9 MB; entries beyond the cap are dropped and |
Source: cmd/api/main.go, pkg/api/bins.go
SQS Queues
| Queue | Visibility | Delay | Max Receive | Producers | Consumer |
|---|---|---|---|---|---|
|
900s |
0s |
5 |
webhook; api and listener (synthetic webhooks) |
miner |
|
900s |
30s |
5 |
miner, throttle-miner |
throttle-miner |
|
900s |
0s |
5 |
EventBridge rule |
listener |
|
900s |
0s |
5 |
None in this repo |
None in this repo |
|
Owned by Insight Service |
— |
— |
refiner |
Insight Service |
Each queue owned here has a <queue>-dlq dead-letter queue.
Kinesis
| Stream | Producers | Consumer | Notes |
|---|---|---|---|
|
miner, throttle-miner |
refiner |
Raw Plaid |
|
feeder (IAM and env vars wired) |
None in this repo |
The feeder’s stream filter excludes |
Both streams are data sources provisioned in the infrastructure repo.
EventBridge
Publishers target the account default bus in the deploy region; there is no custom bus and EVENTBRIDGE_AWS_BUS_NAME is unset. Contracts are in Events.
| Bus / Rule | Routing |
|---|---|
|
Auth0 log-stream events |
|
|
Secrets Manager
| Secret | Contents | Consumers |
|---|---|---|
|
Plaid client ID and secret |
api, webhook, miner, throttle-miner, feeder, institution |
|
GrowthBook SDK key and host |
api, miner, throttle-miner |
|
Segment write key |
miner, throttle-miner |
|
Iterable API key |
api |
|
Datadog API and app keys |
Terraform (SLOs, service definition) |
Runtime Configuration
Behavior-affecting environment variables set by Terraform.
| Variable | Lambda | test | prod | Effect |
|---|---|---|---|---|
|
api |
7 |
7 |
Cooldown in Change Check |
|
api |
0 |
1440 |
Account age below which a v1 removal by a user with detected employment is banned |
|
api |
false |
true |
Ban is logged instead of calling the User Service |
|
api |
true |
true |
Change check passes when Insight reports no employment |
|
api |
5 |
1440 |
TTL on items added with |
|
api |
7 |
7 |
TTL on prequal items |
|
refiner, api |
100 |
100 |
Balance snapshot TTL |
|
miner |
true |
false |
Process |
|
miner, throttle-miner |
false |
false |
Set by Terraform; no code path reads them |
|
miner |
false, unset |
false, unset |
Read into the miner struct; no code path evaluates them |
|
listener |
24h |
24h |
Read into the listener struct; the 24-hour freshness window is a code constant |
Source: deploy/lambda.tf, deploy/variables.tf
Monitoring
Datadog monitors and SLOs exist in prod only (local.create_datadog_monitors in deploy/datadog.tf). Alerts outside prod go unread, and the old and new test accounts both deploy as test, so Datadog would reject the second account’s identical monitors as duplicates.
Three Datadog SLOs (Lambda errors, throughput, latency) span all eight functions, each 99.9% target / 99.99% warning over 7-day and 30-day windows. A Datadog service definition is published from deploy/datadog.tf.