# eliau2005/openadsource architecture

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

- Interactive diagram: https://gitdiagram.com/eliau2005/openadsource
- Repository: https://github.com/eliau2005/openadsource

## Overview

OpenAdSource is a self-hosted video ad server with an operator dashboard. Operators manage advertisers, campaigns and ads, upload creative media, and review reports; players request VAST and tracking pixels, while a worker reconciles tracking counters and campaign budgets. The graph separates dashboard workflows, ad decision and response generation, and persistent state/reconciliation. The README documents the broad service-to-store links; sampled Go code confirms the delivery handler’s internal decision stages. Dashboard action internals and several runtime modules were not sampled, so their finer-grained links are omitted.

Main entry points: [`server/internal/delivery/handler.go`](https://github.com/eliau2005/openadsource/blob/main/server/internal/delivery/handler.go) (VAST delivery), [`dashboard/app/(app)/layout.tsx`](https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/layout.tsx) (Dashboard), [`server/cmd/adserver/main.go`](https://github.com/eliau2005/openadsource/blob/main/server/cmd/adserver/main.go) (Adserver routes), [`server/internal/worker/drainer.go`](https://github.com/eliau2005/openadsource/blob/main/server/internal/worker/drainer.go) (Counter drainer)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_dashboard["Operator dashboard"]
  node_dashboard_app["Dashboard<br/>[layout.tsx]"]
  node_advertisers["Advertiser CRUD<br/>[_actions.ts]"]
  node_campaigns["Campaign CRUD<br/>[_actions.ts]"]
  node_ads["Ad CRUD<br/>[_actions.ts]"]
  node_auth["Dashboard auth<br/>[_actions.ts]"]
  node_upload["Creative upload<br/>[route.ts]"]
  node_reports["Campaign reports<br/>[page.tsx]"]
end

subgraph group_serving["Ad serving"]
  node_adserver["Adserver routes<br/>[main.go]"]
  node_delivery["VAST delivery<br/>[handler.go]"]
  node_registry["Ad registry<br/>[loader.go]"]
  node_selection["Ad selection<br/>[select.go]"]
  node_targeting["Geo and device"]
  node_capping["Budget and frequency"]
  node_vast["VAST builder<br/>[builder.go]"]
  node_media_resolver["Media resolver<br/>[resolver.go]"]
end

subgraph group_reconcile["Tracking and reconciliation"]
  node_tracking["Pixel tracking<br/>[handler.go]"]
  node_worker["Reconciler worker<br/>[main.go]"]
  node_drainer["Counter drainer<br/>[drainer.go]"]
end

subgraph group_state["State and media"]
  node_postgres[("Postgres")]
  node_redis[("Redis")]
  node_s3[("S3-compatible media")]
end

node_operator(("Operator"))
node_player(("Video player"))

node_operator -->|"uses"| node_dashboard_app
node_dashboard_app -->|"manages"| node_advertisers
node_dashboard_app -->|"manages"| node_campaigns
node_dashboard_app -->|"manages"| node_ads
node_dashboard_app -.->|"authenticates"| node_auth
node_dashboard_app -->|"views"| node_reports
node_dashboard_app -->|"requests upload URL"| node_upload
node_advertisers -->|"stores records"| node_postgres
node_campaigns -->|"stores records"| node_postgres
node_ads -->|"stores records"| node_postgres
node_reports -.->|"reads reports"| node_postgres
node_upload -->|"presigns upload"| node_s3
node_player -->|"requests VAST and pixels"| node_adserver
node_adserver -->|"routes VAST"| node_delivery
node_adserver -->|"routes tracking"| node_tracking
node_delivery -->|"reads snapshot"| node_registry
node_delivery -->|"derives targeting"| node_targeting
node_delivery -->|"selects candidates"| node_selection
node_delivery -->|"checks caps"| node_capping
node_delivery -->|"builds response"| node_vast
node_delivery -.->|"resolves media"| node_media_resolver
node_delivery -->|"reserves budget"| node_redis
node_adserver -->|"reloads registry"| node_postgres
node_adserver -->|"receives invalidations"| node_redis
node_media_resolver -.->|"resolves stored media"| node_s3
node_tracking -->|"increments counters"| node_redis
node_worker -->|"runs ticks"| node_drainer
node_drainer -->|"drains counters"| node_redis
node_drainer -->|"upserts stats and pauses campaigns"| node_postgres
node_drainer -->|"publishes invalidation"| node_redis

click node_dashboard_app "https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/layout.tsx"
click node_advertisers "https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/advertisers/_actions.ts"
click node_campaigns "https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/campaigns/_actions.ts"
click node_ads "https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/ads/_actions.ts"
click node_auth "https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(auth)/login/_actions.ts"
click node_upload "https://github.com/eliau2005/openadsource/blob/main/dashboard/app/api/upload/route.ts"
click node_reports "https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/reports/%5BcampaignId%5D/page.tsx"
click node_adserver "https://github.com/eliau2005/openadsource/blob/main/server/cmd/adserver/main.go"
click node_delivery "https://github.com/eliau2005/openadsource/blob/main/server/internal/delivery/handler.go"
click node_registry "https://github.com/eliau2005/openadsource/blob/main/server/internal/registry/loader.go"
click node_selection "https://github.com/eliau2005/openadsource/blob/main/server/internal/selection/select.go"
click node_targeting "https://github.com/eliau2005/openadsource/tree/main/server/internal/targeting"
click node_capping "https://github.com/eliau2005/openadsource/tree/main/server/internal/capping"
click node_vast "https://github.com/eliau2005/openadsource/blob/main/server/internal/vast/builder.go"
click node_media_resolver "https://github.com/eliau2005/openadsource/blob/main/server/internal/storage/resolver.go"
click node_tracking "https://github.com/eliau2005/openadsource/blob/main/server/internal/tracking/handler.go"
click node_worker "https://github.com/eliau2005/openadsource/blob/main/server/cmd/worker/main.go"
click node_drainer "https://github.com/eliau2005/openadsource/blob/main/server/internal/worker/drainer.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_dashboard_app,node_advertisers,node_campaigns,node_ads,node_auth,node_upload,node_reports toneBlue
class node_adserver,node_delivery,node_registry,node_selection,node_targeting,node_capping,node_vast,node_media_resolver toneAmber
class node_tracking,node_worker,node_drainer toneMint
class node_postgres,node_redis,node_s3 toneRose
class node_operator,node_player toneIndigo
```

## Components

### Operator dashboard

- [Dashboard](https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/layout.tsx): `dashboard/app/(app)/layout.tsx`
- [Advertiser CRUD](https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/advertisers/_actions.ts): `dashboard/app/(app)/advertisers/_actions.ts`
- [Campaign CRUD](https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/campaigns/_actions.ts): `dashboard/app/(app)/campaigns/_actions.ts`
- [Ad CRUD](https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/ads/_actions.ts): `dashboard/app/(app)/ads/_actions.ts`
- [Dashboard auth](https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(auth)/login/_actions.ts): `dashboard/app/(auth)/login/_actions.ts`
- [Creative upload](https://github.com/eliau2005/openadsource/blob/main/dashboard/app/api/upload/route.ts): `dashboard/app/api/upload/route.ts`
- [Campaign reports](https://github.com/eliau2005/openadsource/blob/main/dashboard/app/(app)/reports/%5BcampaignId%5D/page.tsx): `dashboard/app/(app)/reports/[campaignId]/page.tsx`

### Ad serving

- [Adserver routes](https://github.com/eliau2005/openadsource/blob/main/server/cmd/adserver/main.go): `server/cmd/adserver/main.go`
- [VAST delivery](https://github.com/eliau2005/openadsource/blob/main/server/internal/delivery/handler.go): `server/internal/delivery/handler.go`
- [Ad registry](https://github.com/eliau2005/openadsource/blob/main/server/internal/registry/loader.go): `server/internal/registry/loader.go`
- [Ad selection](https://github.com/eliau2005/openadsource/blob/main/server/internal/selection/select.go): `server/internal/selection/select.go`
- [Geo and device](https://github.com/eliau2005/openadsource/tree/main/server/internal/targeting): `server/internal/targeting`
- [Budget and frequency](https://github.com/eliau2005/openadsource/tree/main/server/internal/capping): `server/internal/capping`
- [VAST builder](https://github.com/eliau2005/openadsource/blob/main/server/internal/vast/builder.go): `server/internal/vast/builder.go`
- [Media resolver](https://github.com/eliau2005/openadsource/blob/main/server/internal/storage/resolver.go): `server/internal/storage/resolver.go`

### Tracking and reconciliation

- [Pixel tracking](https://github.com/eliau2005/openadsource/blob/main/server/internal/tracking/handler.go): `server/internal/tracking/handler.go`
- [Reconciler worker](https://github.com/eliau2005/openadsource/blob/main/server/cmd/worker/main.go): `server/cmd/worker/main.go`
- [Counter drainer](https://github.com/eliau2005/openadsource/blob/main/server/internal/worker/drainer.go): `server/internal/worker/drainer.go`

### State and media

- **Postgres**
- **Redis**
- **S3-compatible media**

### Other components

- **Operator**
- **Video player**

## Connections

- Operator → Dashboard: uses (evidence: `README.md`)
- Dashboard → Advertiser CRUD: manages (evidence: `README.md`)
- Dashboard → Campaign CRUD: manages (evidence: `README.md`)
- Dashboard → Ad CRUD: manages (evidence: `README.md`)
- Dashboard → Dashboard auth: authenticates
- Dashboard → Campaign reports: views (evidence: `README.md`)
- Dashboard → Creative upload: requests upload URL (evidence: `dashboard/app/api/upload/route.ts`)
- Advertiser CRUD → Postgres: stores records (evidence: `README.md`)
- Campaign CRUD → Postgres: stores records (evidence: `README.md`)
- Ad CRUD → Postgres: stores records (evidence: `README.md`)
- Campaign reports → Postgres: reads reports
- Creative upload → S3-compatible media: presigns upload (evidence: `dashboard/app/api/upload/route.ts`)
- Video player → Adserver routes: requests VAST and pixels (evidence: `README.md`)
- Adserver routes → VAST delivery: routes VAST (evidence: `server/cmd/adserver/main.go`)
- Adserver routes → Pixel tracking: routes tracking (evidence: `server/cmd/adserver/main.go`)
- VAST delivery → Ad registry: reads snapshot (evidence: `server/internal/delivery/handler.go`)
- VAST delivery → Geo and device: derives targeting (evidence: `server/internal/delivery/handler.go`)
- VAST delivery → Ad selection: selects candidates (evidence: `server/internal/delivery/handler.go`)
- VAST delivery → Budget and frequency: checks caps (evidence: `server/internal/delivery/handler.go`)
- VAST delivery → VAST builder: builds response (evidence: `server/internal/delivery/handler.go`)
- VAST delivery → Media resolver: resolves media
- VAST delivery → Redis: reserves budget (evidence: `README.md`)
- Adserver routes → Postgres: reloads registry (evidence: `README.md`)
- Adserver routes → Redis: receives invalidations (evidence: `README.md`)
- Media resolver → S3-compatible media: resolves stored media
- Pixel tracking → Redis: increments counters (evidence: `server/internal/tracking/handler.go`)
- Reconciler worker → Counter drainer: runs ticks (evidence: `server/cmd/worker/main.go`)
- Counter drainer → Redis: drains counters (evidence: `server/internal/worker/drainer.go`)
- Counter drainer → Postgres: upserts stats and pauses campaigns (evidence: `server/internal/worker/drainer.go`)
- Counter drainer → Redis: publishes invalidation (evidence: `server/internal/worker/drainer.go`)
