# billing-service architecture `billing-service` is the billing write model in the Cloud Network Billing & Control Plane. It consumes normalized traffic snapshots, computes replay-safe minute deltas, and writes billing truth into PostgreSQL. Code-level references for the current implementation: - [design.md](design.md) - [reference/service.md](reference/service.md) - [reference/repository.md](reference/repository.md) - [reference/httpapi.md](reference/httpapi.md) ## Deployment topology ```mermaid flowchart TB subgraph Node["Single-node VPS / billing host"] Agent["agent-svc-plus"] Exporter["xray-exporter"] Billing["billing-service"] StunnelClient["stunnel-client"] end subgraph DBHost["postgresql.svc.plus"] StunnelServer["stunnel-server"] PostgreSQL["PostgreSQL"] end subgraph ReadPath["Read and presentation"] Accounts["accounts.svc.plus"] Console["console.svc.plus"] end Agent --> Billing Agent -. coordination .-> Exporter Billing --> StunnelClient StunnelClient --> StunnelServer StunnelServer --> PostgreSQL Accounts --> PostgreSQL Console --> Accounts ``` ## Data flow ```mermaid flowchart LR Xray["xray-core
raw cumulative counters"] Exporter["xray-exporter
translation layer"] Billing["billing-service
minute delta + rating writer"] PostgreSQL["PostgreSQL
billing source of truth"] Accounts["accounts.svc.plus
read model API"] Console["console.svc.plus
presentation"] Agent["agent-svc-plus
control plane"] Xray -->|"raw per-UUID totals"| Exporter Exporter -->|"GET /v1/snapshots/window"| Billing Agent -->|"schedule collect / reconcile"| Billing Billing -->|"idempotent writes"| PostgreSQL PostgreSQL -->|"usage + ledger + quota facts"| Accounts Accounts -->|"account usage / billing summary APIs"| Console ``` ## Role boundaries - `agent-svc-plus`: control plane scheduling, reconciliation triggers, and future automation hooks - `xray-exporter`: collection and translation layer; it exposes normalized snapshots and Prometheus metrics - `billing-service`: billing writer; it computes positive minute deltas and persists replay-safe facts - `accounts.svc.plus`: PostgreSQL-backed read model; it aggregates usage, billing, and quota state for user-facing APIs - `console.svc.plus`: presentation layer; it reads from `accounts.svc.plus` only ## Shared database contract `billing-service` and `accounts.svc.plus` share the same account database and schema. - the database name remains `account` - on `jp-xhttp-contabo.svc.plus`, `accounts.svc.plus` reaches it through `stunnel-client:15432` - `billing-service` must point `DATABASE_URL` at that same PostgreSQL target so writes and reads stay in one source of truth ## Current implementation vs target architecture ### Current implementation - `billing-service` loads one or more sources from `EXPORTER_SOURCES_JSON` - if `EXPORTER_SOURCES_JSON` is absent, it still accepts a single `EXPORTER_BASE_URL` as a compatibility path - the upstream snapshot source is `GET /v1/snapshots/window` - the service is a task-oriented writer with health, status, and job endpoints - persisted facts land in the existing `accounts.svc.plus` accounting schema ### Target architecture - `billing-service` remains the write model, but evolves into a multi-node aggregation point - the write path handles multiple exporter feeds or equivalent multi-node sample sets without losing `node_id`, `env`, or `inbound_tag` - remote exporter ingestion must work over HTTPS because exporters are not guaranteed to live on the same private network - the target pull contract must keep source checkpoints and replay-safe catch-up explicit and observable across nodes - `accounts.svc.plus` stays the read model and never delegates user-facing usage/billing reads back to `billing-service` ## Target multi-node ingress requirements For the target architecture, `billing-service` should treat exporter nodes as remote sources, not implicit local sidecars. - upstream pulls should use HTTPS with certificate validation enabled - prefer mTLS between `billing-service` and each `xray-exporter` - if mTLS is not ready, use HTTPS plus per-source bearer credentials - source progress must be tracked per exporter node so retries and catch-up stay bounded and observable - billing completeness must come from windowed, replay-safe collection, not from assuming the newest snapshot implies nothing was missed - minute-level sync drift is acceptable; the target is short-window eventual consistency rather than second-level strong consistency ## Invariants - PostgreSQL is the only billing source of truth - `billing-service` and `accounts.svc.plus` share the same `account` database - Prometheus and Grafana remain observability only - `console.svc.plus` does not read PostgreSQL or `billing-service` directly - `accounts.svc.plus` does not use Prometheus as a billing data source