Deployment Overview

PackyTrace has two deployment tracks:

  • The application runs on AWS: one EC2 instance, Docker Compose, Caddy TLS, and Terraform. This is the accepted deployment decision in ADR-014.
  • The documentation runs on Cloudflare Pages: MkDocs is built in GitHub Actions and uploaded to Cloudflare Pages.

The split is deliberate. The application needs containers, Postgres, a broker, and Keycloak. The docs are static files and should not consume the AWS box.

What Was Built

flowchart LR dev[Developer] --> gh[GitHub] gh --> rel[semantic-release] rel --> images[GHCR images] gh --> docs_ci[Docs deploy workflow] docs_ci --> cf[Cloudflare Pages] tf[Terraform] --> aws[AWS] aws --> ec2[One EC2 instance] images --> ec2 ec2 --> app[PackyTrace app] user[Browser scanner] --> app reader[Docs reader] --> cf

The AWS deployment is in deployment/aws. The docs deployment workflow is .github/workflows/deploy-docs-cloudflare.yml.

Cloud Platform Choice

AWS was chosen for the application because the project needed to run on a real public URL within the AWS Free Plan / credit budget. Managed services such as RDS, MSK, ECS Fargate, load balancers, and EKS would consume the budget too quickly. The cheaper shape is one EC2 instance that runs the existing containers.

Cloudflare Pages was chosen only for documentation because it is free for static sites, simple, and independent from the application runtime.

Runtime Topology

flowchart TB internet[Internet] --> caddy[Caddy on EC2<br/>ports 80 and 443] caddy --> spa[Static Svelte app<br/>/srv/www] caddy --> gateway[api-gateway] gateway --> passport[passport-service] gateway --> fridge[fridge-service] gateway --> identity[identity-service] gateway --> personalization[personalization-service] gateway --> analytics[brand-analytics-service] passport --> postgres[(Postgres)] fridge --> postgres identity --> postgres personalization --> postgres analytics --> postgres identity --> keycloak[Keycloak] keycloak --> postgres passport --> redpanda[Redpanda<br/>Kafka API] fridge --> redpanda identity --> redpanda personalization --> redpanda measurement[measurement-pipeline] --> redpanda redpanda --> analytics

Only Caddy is public. Postgres, Redpanda, Keycloak, and all seven services are private inside the Docker network.

Microservice Split

The platform is split by responsibility, not by technical layer.

Service Purpose Stack
api-gateway Public entry point, routing, token validation Go
passport-service GS1 resolver, product catalog, scan records Go
fridge-service Fridge items, expiry checks, waste summary Go
measurement-pipeline Consumer-side scan facts and aggregation pipeline Go
identity-service Visitors, accounts, consent, Keycloak integration TypeScript
personalization-service Health profiles and verdict rules TypeScript
brand-analytics-service Privacy-preserving brand aggregates TypeScript

The split preserves the main architecture rules:

  • services share contracts, not code;
  • every service owns its own database schema;
  • events use JSON Schema contracts;
  • raw consumer facts do not cross into brand analytics.

Main User Flow

sequenceDiagram participant Browser participant Caddy participant Gateway as api-gateway participant Passport as passport-service participant Personalization as personalization-service participant DB as Postgres participant Broker as Redpanda Browser->>Caddy: GET /01/{gtin} Caddy->>Gateway: proxy resolver request Gateway->>Passport: resolve scan Passport->>DB: read product catalog and write scan Passport->>Personalization: request verdict Personalization->>DB: read health profile Personalization-->>Passport: verdict Passport->>Broker: publish scan/verdict facts Passport-->>Gateway: resolved passport Gateway-->>Caddy: response Caddy-->>Browser: product passport page

Pages In This Section