# moov-io/achgateway architecture

> Architecture diagram of the GitHub repository moov-io/achgateway, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-08-13). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/moov-io/achgateway
- Repository: https://github.com/moov-io/achgateway

## Overview

#### Purpose and runtime
ACH Gateway is a Go service that accepts ACH files over HTTP and Kafka, queues and transforms them, then uploads optimized files to an ODFI at configured cutoff times. It also retrieves and processes inbound ODFI files (returns, reconciliations, corrections, prenotes). The executable entry point is `cmd/achgateway/main.go`; service assembly and HTTP serving are centered in `internal/server.go` and `internal/environment.go`.

#### Service configuration and tenancy
Configuration defines storage, database, inbound/outbound transport, schedules, events, notifications, encryption, and shard mappings. Shards isolate tenant/file-processing scope and map requests or Kafka records to processing configuration. Core paths: `configs/config.default.yml`, `internal/service/model_config.go`, `internal/shards/service_shard_mapping.go`. Persistent shard and file metadata is supported through MySQL or Google Cloud Spanner migrations in `migrations/001_shard_mappings.up.mysql.sql`, `migrations/002_files.up.spanner.sql`.

#### File ingestion boundary
Public ingestion accepts Nacha ACH or Moov ACH JSON files under shard-scoped HTTP APIs, validates/normalizes them, and places accepted files into repository-backed pending storage. Kafka can provide the same asynchronous input channel. Relevant paths: `internal/incoming/web/api_files.go`, `internal/pipeline/file_receiver.go`, `internal/kafka/kafka.go`. This boundary separates caller-facing receipt from deferred cutoff-time delivery.

#### Outbound pipeline
The pipeline coordinates pending-file discovery, aggregation/merging, cutoff execution, formatting, transformation, and transfer. At cutoff, eligible shard files are merged where configured, rendered as Nacha or encoded/encrypted output, then sent through a selected transfer agent. Main orchestration paths: `internal/pipeline/pipeline.go`, `internal/pipeline/merging.go`, `internal/pipeline/aggregate.go`. Scheduling is implemented in `internal/schedule/cutoff.go` and manually triggerable through `internal/pipeline/manual_cutoff_times.go`.

#### Storage, security, and delivery adapters
Storage is abstracted behind blob/filesystem implementations and may encrypt persisted data. Before upload, files can be transformed with GPG; destination delivery supports FTP and SFTP, including retry and network-security handling. Paths: `internal/storage/storage.go`, `internal/transform/gpg.go`, `internal/upload/ftp.go`, `internal/upload/sftp.go`. These adapters are the primary boundaries to local/blob storage, cryptographic key material, and ODFI-hosted file servers.

#### Inbound ODFI processing
A periodic or manually triggered processor downloads remote inbound directories, classifies ACH artifacts, emits domain outcomes, and cleans up remote files after handling. ODFI-specific flows cover returns, corrections, prenotes, reconciliation, audits, and download lifecycle. Paths: `internal/incoming/odfi/incoming.go`, `internal/incoming/odfi/processor.go`, `internal/incoming/odfi/returns.go`, `internal/incoming/odfi/cleanup.go`.

#### Events, audit, and operational integrations
Domain events are delivered via stream/Kafka or webhooks; audit records can be retained through blob storage. Notifications and alerts target email, Kafka, Slack, and PagerDuty. Prometheus metrics are served separately on the metrics/admin listener, with manual operational triggers including cutoff and inbound processing. Paths: `internal/events/service.go`, `internal/events/service_webhook.go`, `internal/audittrail/storage_blob.go`, `internal/notify/multi.go`, `internal/alerting/alerter.go`.

#### Deployment
The service targets Go 1.19+ and is distributed as a Docker image. Local/container deployment is defined by `Dockerfile`, `docker-compose.yml`, and `examples/getting-started/docker-compose.yml`; default Docker-oriented configuration is in `configs/config.docker.yml`.

Main entry points: [`internal/pipeline/pipeline.go`](https://github.com/moov-io/achgateway/blob/master/internal/pipeline/pipeline.go) (Outbound pipeline), [`internal/shards/service_shard_mapping.go`](https://github.com/moov-io/achgateway/blob/master/internal/shards/service_shard_mapping.go) (Shard mapping), [`internal/server.go`](https://github.com/moov-io/achgateway/blob/master/internal/server.go) (Service assembly & HTTP), [`internal/incoming/web/api_files.go`](https://github.com/moov-io/achgateway/blob/master/internal/incoming/web/api_files.go) (Shard-scoped file API)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_runtime["Service Runtime"]
  node_entrypoint["ACH Gateway executable<br/>Go entry point<br/>[main.go]"]
  node_server["Service assembly &amp; HTTP<br/>server<br/>[server.go]"]
  node_environment["Runtime environment<br/>[environment.go]"]
  node_config["Gateway configuration<br/>configuration model<br/>[model_config.go]"]
  node_shards["Shard mapping<br/>tenant routing"]
  node_metadata[("Shard &amp; file metadata<br/>persistent metadata")]
end

subgraph group_ingestion["File Ingestion"]
  node_http_api["Shard-scoped file API<br/>HTTP ingestion<br/>[api_files.go]"]
  node_kafka_input["Kafka input<br/>Kafka consumer<br/>[kafka.go]"]
  node_receiver["File receiver<br/>validation &amp; normalization<br/>[file_receiver.go]"]
  node_storage[("Pending file storage<br/>storage abstraction<br/>[storage.go]")]
end

subgraph group_outbound["Outbound Delivery"]
  node_cutoff(("Cutoff scheduler<br/>[cutoff.go]"))
  node_manual_cutoff["Manual cutoff trigger<br/>operational trigger"]
  node_pipeline["Outbound pipeline<br/>cutoff orchestration<br/>[pipeline.go]"]
  node_merging["Merge &amp; aggregate<br/>file aggregation<br/>[merging.go]"]
  node_transform["Output transform<br/>GPG encryption<br/>[gpg.go]"]
  node_transfer["ODFI transfer agents<br/>FTP/SFTP delivery<br/>[sftp.go]"]
end

subgraph group_inbound["Inbound ODFI"]
  node_inbound_processor["Inbound ODFI processor<br/>remote file processor<br/>[processor.go]"]
  node_inbound_flow["Inbound outcomes<br/>returns &amp; reconciliation<br/>[returns.go]"]
end

subgraph group_operations["Operations"]
  node_events["Domain event delivery<br/>Kafka/webhook events<br/>[service.go]"]
  node_audit[("Audit trail<br/>blob audit storage<br/>[storage_blob.go]")]
  node_notifications["Notifications &amp; alerts<br/>operational integrations<br/>[multi.go]"]
end

node_entrypoint -->|"starts"| node_server
node_server -->|"initializes"| node_environment
node_environment -->|"loads"| node_config
node_config -->|"defines"| node_shards
node_shards -->|"persists mappings"| node_metadata
node_server -->|"serves"| node_http_api
node_http_api -->|"resolves shard"| node_shards
node_http_api -->|"submits file"| node_receiver
node_kafka_input -->|"resolves shard"| node_shards
node_kafka_input -->|"submits file"| node_receiver
node_receiver -->|"stores pending file"| node_storage
node_cutoff -->|"triggers"| node_pipeline
node_manual_cutoff -.->|"triggers"| node_pipeline
node_pipeline -->|"discovers pending files"| node_storage
node_pipeline -->|"aggregates eligible files"| node_merging
node_merging -->|"renders output"| node_transform
node_transform -->|"sends prepared file"| node_transfer
node_inbound_processor -->|"downloads from ODFI"| node_transfer
node_inbound_processor -->|"classifies and processes"| node_inbound_flow
node_pipeline -->|"emits outcomes"| node_events
node_inbound_flow -->|"emits outcomes"| node_events
node_pipeline -->|"records audit data"| node_audit
node_events -->|"drives alerts"| node_notifications

click node_entrypoint "https://github.com/moov-io/achgateway/blob/master/cmd/achgateway/main.go"
click node_server "https://github.com/moov-io/achgateway/blob/master/internal/server.go"
click node_environment "https://github.com/moov-io/achgateway/blob/master/internal/environment.go"
click node_config "https://github.com/moov-io/achgateway/blob/master/internal/service/model_config.go"
click node_shards "https://github.com/moov-io/achgateway/blob/master/internal/shards/service_shard_mapping.go"
click node_metadata "https://github.com/moov-io/achgateway/blob/master/migrations/001_shard_mappings.up.mysql.sql"
click node_http_api "https://github.com/moov-io/achgateway/blob/master/internal/incoming/web/api_files.go"
click node_kafka_input "https://github.com/moov-io/achgateway/blob/master/internal/kafka/kafka.go"
click node_receiver "https://github.com/moov-io/achgateway/blob/master/internal/pipeline/file_receiver.go"
click node_storage "https://github.com/moov-io/achgateway/blob/master/internal/storage/storage.go"
click node_cutoff "https://github.com/moov-io/achgateway/blob/master/internal/schedule/cutoff.go"
click node_manual_cutoff "https://github.com/moov-io/achgateway/blob/master/internal/pipeline/manual_cutoff_times.go"
click node_pipeline "https://github.com/moov-io/achgateway/blob/master/internal/pipeline/pipeline.go"
click node_merging "https://github.com/moov-io/achgateway/blob/master/internal/pipeline/merging.go"
click node_transform "https://github.com/moov-io/achgateway/blob/master/internal/transform/gpg.go"
click node_transfer "https://github.com/moov-io/achgateway/blob/master/internal/upload/sftp.go"
click node_inbound_processor "https://github.com/moov-io/achgateway/blob/master/internal/incoming/odfi/processor.go"
click node_inbound_flow "https://github.com/moov-io/achgateway/blob/master/internal/incoming/odfi/returns.go"
click node_events "https://github.com/moov-io/achgateway/blob/master/internal/events/service.go"
click node_audit "https://github.com/moov-io/achgateway/blob/master/internal/audittrail/storage_blob.go"
click node_notifications "https://github.com/moov-io/achgateway/blob/master/internal/notify/multi.go"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_entrypoint,node_server,node_environment,node_config,node_shards,node_metadata toneBlue
class node_http_api,node_kafka_input,node_receiver,node_storage toneAmber
class node_cutoff,node_manual_cutoff,node_pipeline,node_merging,node_transform,node_transfer toneMint
class node_inbound_processor,node_inbound_flow toneRose
class node_events,node_audit,node_notifications toneIndigo
```

## Components

### Service Runtime

- [ACH Gateway executable](https://github.com/moov-io/achgateway/blob/master/cmd/achgateway/main.go): Go entry point · `cmd/achgateway/main.go`
- [Service assembly & HTTP](https://github.com/moov-io/achgateway/blob/master/internal/server.go): server · Builds service dependencies and serves public/admin listeners. · `internal/server.go`
- [Runtime environment](https://github.com/moov-io/achgateway/blob/master/internal/environment.go): environment · `internal/environment.go`
- [Gateway configuration](https://github.com/moov-io/achgateway/blob/master/internal/service/model_config.go): configuration model · Defines transports, schedules, security, events, and shard mappings. · `internal/service/model_config.go`
- [Shard mapping](https://github.com/moov-io/achgateway/blob/master/internal/shards/service_shard_mapping.go): tenant routing · Maps HTTP requests and Kafka records to tenant processing configuration. · `internal/shards/service_shard_mapping.go`
- [Shard & file metadata](https://github.com/moov-io/achgateway/blob/master/migrations/001_shard_mappings.up.mysql.sql): persistent metadata · MySQL or Spanner stores shard and file state. · `migrations/001_shard_mappings.up.mysql.sql`

### File Ingestion

- [Shard-scoped file API](https://github.com/moov-io/achgateway/blob/master/internal/incoming/web/api_files.go): HTTP ingestion · `internal/incoming/web/api_files.go`
- [Kafka input](https://github.com/moov-io/achgateway/blob/master/internal/kafka/kafka.go): Kafka consumer · `internal/kafka/kafka.go`
- [File receiver](https://github.com/moov-io/achgateway/blob/master/internal/pipeline/file_receiver.go): validation & normalization · Accepts Nacha or Moov ACH JSON and queues valid files. · `internal/pipeline/file_receiver.go`
- [Pending file storage](https://github.com/moov-io/achgateway/blob/master/internal/storage/storage.go): storage abstraction · Blob or filesystem storage can encrypt persisted files. · `internal/storage/storage.go`

### Outbound Delivery

- [Cutoff scheduler](https://github.com/moov-io/achgateway/blob/master/internal/schedule/cutoff.go): scheduler · `internal/schedule/cutoff.go`
- [Manual cutoff trigger](https://github.com/moov-io/achgateway/blob/master/internal/pipeline/manual_cutoff_times.go): operational trigger · `internal/pipeline/manual_cutoff_times.go`
- [Outbound pipeline](https://github.com/moov-io/achgateway/blob/master/internal/pipeline/pipeline.go): cutoff orchestration · Discovers eligible files and coordinates delivery processing. · `internal/pipeline/pipeline.go`
- [Merge & aggregate](https://github.com/moov-io/achgateway/blob/master/internal/pipeline/merging.go): file aggregation · Combines eligible shard files where configured. · `internal/pipeline/merging.go`
- [Output transform](https://github.com/moov-io/achgateway/blob/master/internal/transform/gpg.go): GPG encryption · Renders or encrypts output before transfer. · `internal/transform/gpg.go`
- [ODFI transfer agents](https://github.com/moov-io/achgateway/blob/master/internal/upload/sftp.go): FTP/SFTP delivery · Uploads optimized files with retry and network-security handling. · `internal/upload/sftp.go`

### Inbound ODFI

- [Inbound ODFI processor](https://github.com/moov-io/achgateway/blob/master/internal/incoming/odfi/processor.go): remote file processor · Downloads, classifies, handles, and cleans up inbound ACH artifacts. · `internal/incoming/odfi/processor.go`
- [Inbound outcomes](https://github.com/moov-io/achgateway/blob/master/internal/incoming/odfi/returns.go): returns & reconciliation · Processes returns, corrections, prenotes, reconciliation, and audits. · `internal/incoming/odfi/returns.go`

### Operations

- [Domain event delivery](https://github.com/moov-io/achgateway/blob/master/internal/events/service.go): Kafka/webhook events · `internal/events/service.go`
- [Audit trail](https://github.com/moov-io/achgateway/blob/master/internal/audittrail/storage_blob.go): blob audit storage · `internal/audittrail/storage_blob.go`
- [Notifications & alerts](https://github.com/moov-io/achgateway/blob/master/internal/notify/multi.go): operational integrations · Delivers email, Kafka, Slack, and PagerDuty notifications. · `internal/notify/multi.go`

## Connections

- ACH Gateway executable → Service assembly & HTTP: starts
- Service assembly & HTTP → Runtime environment: initializes
- Runtime environment → Gateway configuration: loads
- Gateway configuration → Shard mapping: defines
- Shard mapping → Shard & file metadata: persists mappings
- Service assembly & HTTP → Shard-scoped file API: serves
- Shard-scoped file API → Shard mapping: resolves shard
- Shard-scoped file API → File receiver: submits file
- Kafka input → Shard mapping: resolves shard
- Kafka input → File receiver: submits file
- File receiver → Pending file storage: stores pending file
- Cutoff scheduler → Outbound pipeline: triggers
- Manual cutoff trigger → Outbound pipeline: triggers
- Outbound pipeline → Pending file storage: discovers pending files
- Outbound pipeline → Merge & aggregate: aggregates eligible files
- Merge & aggregate → Output transform: renders output
- Output transform → ODFI transfer agents: sends prepared file
- Inbound ODFI processor → ODFI transfer agents: downloads from ODFI
- Inbound ODFI processor → Inbound outcomes: classifies and processes
- Outbound pipeline → Domain event delivery: emits outcomes
- Inbound outcomes → Domain event delivery: emits outcomes
- Outbound pipeline → Audit trail: records audit data
- Domain event delivery → Notifications & alerts: drives alerts
