Documentation
¶
Overview ¶
Package ossattr is the pure, deterministic attribution core of the SBOM-driven OSS-developer payout system. It answers exactly one question:
Given an org's Hanzo cloud spend and the set of OSS packages present in the SBOM(s) of what that org runs, how much of that spend is owed to each OSS package — under a capped, weighted, pro-rata policy?
It is a LEAF package: it imports only the standard library, so the attribution math is independent of commerce server internals, the datastore, ZAP, or HTTP. That makes the model the one place the fairness rules live, and makes it exhaustively testable as a pure function.
Design (one way, decomplected):
The POOL is a fraction of spend, hard-capped at 25%. Hanzo keeps the rest. The cap is the headline promise ("up to 25% of all Hanzo cloud costs go to upstream OSS") and is enforced here, in code, not by convention. Pool = spend * min(PoolFraction, MaxPoolFraction).
Each package gets a WEIGHT from the policy. There is exactly one mechanism — pro-rata by weight — and the policy supplies the weights. Direct vs transitive is not a special case baked into the mechanism; it is two numbers in the policy (DirectWeight, TransitiveWeight). A per-package criticality multiplier is also just data. This keeps the mechanism (split a pool pro-rata) orthogonal to the fairness knobs.
A package's share = Pool * (weight_i / Σ weight_j).
The split is in integer cents and conserves the pool exactly: the sum of allocated cents equals the pool to the last cent (largest-remainder apportionment), so no money is created or lost to rounding.
The function is total and deterministic: identical inputs always produce identical outputs, including a stable ordering, so the same accrual is reproducible and auditable.
Index ¶
Constants ¶
const MaxPoolFraction = 0.25
MaxPoolFraction is the absolute ceiling on the OSS pool: at most 25% of an org's Hanzo cloud spend is ever attributed to OSS dependencies. Any policy requesting more is clamped to this value. This constant IS the "up to 25%" promise; it lives in code so it cannot silently drift.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Allocation ¶
type Allocation struct {
PURL string
Name string
Ecosystem string
Version string
Scope Scope
Weight float64 // final weight used (base*criticality)
Cents int64 // attributed amount, integer cents (apportioned)
}
Allocation is the attribution result for one package: how many cents of the pool it earns, plus the inputs that produced it (for a fully auditable ledger line — anyone can recompute share = weight/totalWeight * pool).
type Package ¶
type Package struct {
PURL string // canonical Package URL — the identity
Name string // human name, e.g. "github.com/gin-gonic/gin"
Ecosystem string // "golang" | "npm" | "pypi" | "apk" | "deb" | ...
Version string // resolved version, e.g. "v1.12.0"
Scope Scope // direct | transitive
Criticality float64 // policy multiplier; 0 or negative means "use 1.0"
}
Package is one OSS dependency observed in an SBOM. It is identified by its Package URL (PURL, https://github.com/package-url/purl-spec), the ecosystem- neutral coordinate emitted by CycloneDX/SPDX (e.g. "pkg:golang/github.com/gin-gonic/gin@v1.12.0", "pkg:npm/react@18.3.1", "pkg:pypi/fastapi@0.110.0"). The PURL is the join key across SBOMs, the accrual ledger, and the maintainer resolver.
type Policy ¶
type Policy struct {
// PoolFraction is the share of spend allocated to the OSS pool, e.g. 0.25
// for the full 25%. Clamped to MaxPoolFraction. A lower value lets Hanzo
// ramp the program up (e.g. 0.10) without ever exceeding the promise.
PoolFraction float64
// DirectWeight and TransitiveWeight are the base per-package weights by
// scope. Defaults: direct = 1.0, transitive = 0.25. A package's final
// weight is base(scope) * criticality.
DirectWeight float64
TransitiveWeight float64
}
Policy parameterizes attribution. All knobs are data; the mechanism is fixed. The zero Policy is NOT valid — callers should start from DefaultPolicy and override. PoolFraction is clamped to [0, MaxPoolFraction].
func DefaultPolicy ¶
func DefaultPolicy() Policy
DefaultPolicy is the canonical starting policy: the full 25% pool, direct deps weighted 4x a transitive dep, criticality off (1.0) by default.
type Result ¶
type Result struct {
SpendCents int64 // the input spend
PoolFraction float64 // the effective (clamped) pool fraction applied
PoolCents int64 // spend * poolFraction, floored — the amount split
TotalWeight float64 // Σ weights over all attributable packages
Allocations []Allocation // per-package, sorted by PURL (stable)
// UnallocatedCents is pool cents that could not be attributed (no packages
// or zero total weight). It is held, not lost — the caller routes it to a
// held pool. Always 0 when there is at least one positively-weighted pkg.
UnallocatedCents int64
}
Result is the full attribution of one spend amount across a package set.
func Attribute ¶
Attribute is the heart of the system: a pure, total, deterministic function from (spend, packages, policy) to per-package cent allocations.
Contract:
- Pool = floor(spendCents * clamp(policy.PoolFraction, 0, 25%)).
- Each package weight = base(scope) * criticality.
- share_i = pool * weight_i / Σ weight_j, apportioned to integer cents by largest remainder so Σ allocated cents == pool exactly.
- Packages are de-duplicated by PURL (the highest-weight instance wins; ties keep the first seen) so the same dep across multiple SBOMs is paid once. Duplicate handling is deterministic.
- With no packages, or all weights zero, the whole pool is Unallocated (held), nothing is invented.
Allocations are returned sorted by PURL for stable, reproducible output.
type Scope ¶
type Scope string
Scope classifies how a package entered the dependency graph. A direct dependency is declared by a deployed Hanzo service; a transitive dependency is pulled in by another dependency. The two carry different default weights (a direct dep is, by default, more load-bearing per-package than any one of the many transitive deps beneath it), but both are paid.