# onellan/openbid architecture

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

- Interactive diagram: https://gitdiagram.com/onellan/openbid
- Repository: https://github.com/onellan/openbid

## Overview

OpenBid is a self-hosted, multi-tenant South African tender aggregation and tracking platform. Users access tender and workspace features through the Go web application; a separate worker synchronizes configured tender sources, queues document extraction, and supports Smart Keyword filtering. Extracted tender details are stored for search and tenant workflows, while configured alerts can be sent by email. The README documents the four-service deployment and extraction modes; sampled source confirms session persistence and the extraction client, but not every route-level call, so uncertain wiring is omitted.

Main entry points: [`internal/store/store.go`](https://github.com/onellan/openbid/blob/main/internal/store/store.go) (Persistence interface), [`internal/worker/runner.go`](https://github.com/onellan/openbid/blob/main/internal/worker/runner.go) (Sync and extraction worker), [`internal/app/app.go`](https://github.com/onellan/openbid/blob/main/internal/app/app.go) (Web application), [`internal/app/alerts.go`](https://github.com/onellan/openbid/blob/main/internal/app/alerts.go) (Alerts)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_access["Web Access"]
  node_webapp["Web application<br/>[app.go]"]
  node_auth["Authentication<br/>[session.go]"]
  node_workspace["Workspace actions<br/>[workspace.go]"]
  node_admin["Source scheduling<br/>[admin.go]"]
end

subgraph group_ingest["Tender Ingestion"]
  node_worker["Sync and extraction worker<br/>[runner.go]"]
  node_sources["Tender source adapters<br/>[source.go]"]
  node_queue["Extraction jobs<br/>[store.go]"]
  node_extract_client["Extractor client<br/>[client.go]"]
  node_extractor["Document extractor<br/>[app.py]"]
end

subgraph group_intelligence["Tender Intelligence"]
  node_smart["Smart Keyword rules<br/>[smart_keywords.go]"]
  node_evaluator["Keyword evaluator<br/>[evaluator.go]"]
  node_search["Tender search<br/>[keyword_search.go]"]
  node_tender_state["Tender lifecycle<br/>[expiry.go]"]
end

subgraph group_state["State and Alerts"]
  node_store["Persistence interface<br/>[store.go]"]
  node_sqlite[("SQLite database<br/>[sqlitestore.go]")]
  node_alerts["Alerts<br/>[alerts.go]"]
  node_mail["Email service<br/>[service.go]"]
end

node_user(("Tenant user"))
node_source_sites(("Tender source sites"))
node_smtp(("SMTP provider"))

node_user -->|"uses"| node_webapp
node_webapp -->|"authenticates"| node_auth
node_webapp -->|"serves"| node_workspace
node_webapp -->|"manages schedules"| node_admin
node_admin -->|"reads and writes"| node_store
node_worker -->|"fetches tenders"| node_sources
node_sources -->|"requests listings"| node_source_sites
node_worker -->|"records sync"| node_store
node_worker -->|"checks extraction rules"| node_evaluator
node_worker -->|"queues extraction"| node_queue
node_queue -->|"persists jobs"| node_store
node_worker -->|"requests extraction"| node_extract_client
node_extract_client -->|"posts document URL"| node_extractor
node_extractor -->|"fetches documents"| node_source_sites
node_smart -->|"manages rules"| node_store
node_search -->|"queries tenders"| node_store
node_workspace -->|"reads and writes"| node_store
node_worker -->|"checks expiry"| node_tender_state
node_worker -->|"persists tenders"| node_store
node_alerts -->|"loads alert data"| node_store
node_alerts -->|"sends notifications"| node_mail
node_mail -.->|"sends email"| node_smtp
node_store -->|"reads and writes"| node_sqlite
node_auth -->|"loads sessions"| node_store

click node_webapp "https://github.com/onellan/openbid/blob/main/internal/app/app.go"
click node_auth "https://github.com/onellan/openbid/blob/main/internal/auth/session.go"
click node_workspace "https://github.com/onellan/openbid/blob/main/internal/app/workspace.go"
click node_admin "https://github.com/onellan/openbid/blob/main/internal/app/admin.go"
click node_worker "https://github.com/onellan/openbid/blob/main/internal/worker/runner.go"
click node_sources "https://github.com/onellan/openbid/blob/main/internal/source/source.go"
click node_queue "https://github.com/onellan/openbid/blob/main/internal/store/store.go"
click node_extract_client "https://github.com/onellan/openbid/blob/main/internal/extract/client.go"
click node_extractor "https://github.com/onellan/openbid/blob/main/extractor/app.py"
click node_smart "https://github.com/onellan/openbid/blob/main/internal/app/smart_keywords.go"
click node_evaluator "https://github.com/onellan/openbid/blob/main/internal/smartkeywords/evaluator.go"
click node_search "https://github.com/onellan/openbid/blob/main/internal/app/keyword_search.go"
click node_tender_state "https://github.com/onellan/openbid/blob/main/internal/tenderstate/expiry.go"
click node_store "https://github.com/onellan/openbid/blob/main/internal/store/store.go"
click node_sqlite "https://github.com/onellan/openbid/blob/main/internal/store/sqlitestore.go"
click node_alerts "https://github.com/onellan/openbid/blob/main/internal/app/alerts.go"
click node_mail "https://github.com/onellan/openbid/blob/main/internal/mail/service.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_webapp,node_auth,node_workspace,node_admin,node_user toneBlue
class node_worker,node_sources,node_queue,node_extract_client,node_extractor toneAmber
class node_smart,node_evaluator,node_search,node_tender_state toneMint
class node_store,node_sqlite,node_alerts,node_mail toneRose
class node_source_sites,node_smtp toneIndigo
```

## Components

### Web Access

- [Web application](https://github.com/onellan/openbid/blob/main/internal/app/app.go): `internal/app/app.go`
- [Authentication](https://github.com/onellan/openbid/blob/main/internal/auth/session.go): `internal/auth/session.go`
- [Workspace actions](https://github.com/onellan/openbid/blob/main/internal/app/workspace.go): `internal/app/workspace.go`
- [Source scheduling](https://github.com/onellan/openbid/blob/main/internal/app/admin.go): `internal/app/admin.go`

### Tender Ingestion

- [Sync and extraction worker](https://github.com/onellan/openbid/blob/main/internal/worker/runner.go): `internal/worker/runner.go`
- [Tender source adapters](https://github.com/onellan/openbid/blob/main/internal/source/source.go): `internal/source/source.go`
- [Extraction jobs](https://github.com/onellan/openbid/blob/main/internal/store/store.go): `internal/store/store.go`
- [Extractor client](https://github.com/onellan/openbid/blob/main/internal/extract/client.go): `internal/extract/client.go`
- [Document extractor](https://github.com/onellan/openbid/blob/main/extractor/app.py): `extractor/app.py`

### Tender Intelligence

- [Smart Keyword rules](https://github.com/onellan/openbid/blob/main/internal/app/smart_keywords.go): `internal/app/smart_keywords.go`
- [Keyword evaluator](https://github.com/onellan/openbid/blob/main/internal/smartkeywords/evaluator.go): `internal/smartkeywords/evaluator.go`
- [Tender search](https://github.com/onellan/openbid/blob/main/internal/app/keyword_search.go): `internal/app/keyword_search.go`
- [Tender lifecycle](https://github.com/onellan/openbid/blob/main/internal/tenderstate/expiry.go): `internal/tenderstate/expiry.go`

### State and Alerts

- [Persistence interface](https://github.com/onellan/openbid/blob/main/internal/store/store.go): `internal/store/store.go`
- [SQLite database](https://github.com/onellan/openbid/blob/main/internal/store/sqlitestore.go): `internal/store/sqlitestore.go`
- [Alerts](https://github.com/onellan/openbid/blob/main/internal/app/alerts.go): `internal/app/alerts.go`
- [Email service](https://github.com/onellan/openbid/blob/main/internal/mail/service.go): `internal/mail/service.go`

### Other components

- **Tenant user**
- **Tender source sites**
- **SMTP provider**

## Connections

- Tenant user → Web application: uses
- Web application → Authentication: authenticates
- Web application → Workspace actions: serves
- Web application → Source scheduling: manages schedules
- Source scheduling → Persistence interface: reads and writes
- Sync and extraction worker → Tender source adapters: fetches tenders
- Tender source adapters → Tender source sites: requests listings
- Sync and extraction worker → Persistence interface: records sync
- Sync and extraction worker → Keyword evaluator: checks extraction rules
- Sync and extraction worker → Extraction jobs: queues extraction
- Extraction jobs → Persistence interface: persists jobs
- Sync and extraction worker → Extractor client: requests extraction
- Extractor client → Document extractor: posts document URL
- Document extractor → Tender source sites: fetches documents
- Smart Keyword rules → Persistence interface: manages rules
- Tender search → Persistence interface: queries tenders
- Workspace actions → Persistence interface: reads and writes
- Sync and extraction worker → Tender lifecycle: checks expiry
- Sync and extraction worker → Persistence interface: persists tenders
- Alerts → Persistence interface: loads alert data
- Alerts → Email service: sends notifications
- Email service → SMTP provider: sends email
- Persistence interface → SQLite database: reads and writes
- Authentication → Persistence interface: loads sessions
