Billing & Metering

Purpose

Billing & Metering enforces quota limits and accrues usage metrics for each tenant. It is designed to be generic (swappable billing backend), event-driven, and non-blocking on the hot commit path — except for the synchronous quota gate at upload initiation.

Data owned

Table Purpose
usage_meters Per-tenant counters: bytes stored, bytes transferred, file count
quotas Per-tenant limits: bytes_limit, bytes_used
plans Plan definitions: storage limit, transfer limit, feature flags

The QUOTA row is the authoritative usage gate: bytes_used is maintained by the Meter worker; bytes_limit is set from the plans table on plan change.

Internal API

Billing.* gRPC methods:

Method Description
Billing.CheckQuota Synchronous gate: is bytes_used + requested_bytes ≤ bytes_limit?
Billing.RecordUsage Record a usage event (async; called by the Meter worker)
Billing.GetUsage Return current usage summary for a tenant
Billing.SetQuota Update quota limits (admin / plan change)

Quota enforcement

Quota is checked synchronously at upload initiation — before a presigned URL is issued. If the check fails (quota exceeded), the upload is rejected with a 429 Quota Exceeded response before any bytes are transferred.

sequenceDiagram
    participant C as Client
    participant GW as API Gateway
    participant BI as Billing
    participant F as File & Metadata

    C->>GW: POST /v1/files (init upload: size)
    GW->>BI: CheckQuota(tenant_id, requested_bytes)
    alt quota available
        BI-->>GW: allow
        GW->>F: CreateUpload(...)
        F-->>GW: uploadId + presigned URL
        GW-->>C: 201 {uploadId, url}
    else quota exceeded
        BI-->>GW: deny (bytes_used + requested > bytes_limit)
        GW-->>C: 429 Quota Exceeded
    end

Usage accrual

Usage accrual is asynchronous — it does not block the commit path:

Plans and limits

Field Description
bytes_limit Maximum bytes stored (enforced synchronously at upload init)
bytes_used Current bytes in committed blobs (updated asynchronously)
file_count_limit Maximum number of nodes (enforced at CreateUpload)
transfer_limit Monthly outbound bytes (informational; enforced at billing cycle)

Plans are stored per tenant in the plans table and are applied at subscription / plan-change time. Changing a plan updates QUOTA.bytes_limit without requiring a deployment.