authors

package
v1.801.381 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2026 License: Apache-2.0 Imports: 27 Imported by: 0

Documentation

Overview

Package authors is a royalty for open-source work: your repo runs, you get paid.

An author links GitHub, proves they own a repo, and earns a royalty on the metered spend of every org that deploys a project built from it — accrued per period and auto-paid, with a full audit trail behind the number.

It is the CREATOR member of the three programs built on the same shape; apps/referrals is the one-time bonus and apps/affiliates the partner commission. All three share the commerce ledger path (a credits payout is a grant, tag grant:author).

The loop, end to end:

  1. An author CONNECTS GitHub (POST /v1/authors/connect): we link the caller's org to a GitHub login — from IAM's linked GitHub account when available (identity verified), else a login the caller supplies (verified per-repo below). We mint a stable per-author VERIFY CODE for the file method.
  2. The author VERIFIES a repo (POST /v1/authors/repos/verify): ownership is proven either by an IAM-linked GitHub token showing ADMIN/PUSH permission on the repo (OAuth method), or by a hanzo.json on the repo's default branch carrying the author's verify code (file method — proves default-branch control). A verified repo can now earn.
  3. When a published project whose sourceRepo matches a VERIFIED author repo is DEPLOYED by ANY org, the deploy path records it (POST /v1/authors/deploys/ record): deploying_org↔repo↔project, idempotent per (repo, project, org). hanzo.app persists sourceRepo on the published project so the deploy is attributable.
  4. The ACCRUAL SWEEP (the scheduler's automatic loop; POST /v1/admin/authors/sweep as an operator override; also lazy on the author's own dashboard read) folds over each approved author's DISTINCT deploying orgs (excluding the author's own): royalty = that org's metered spend THIS PERIOD × the author's share (20%), accrued at-most-once per (author, deploying_org, period).
  5. The AUTO-PAYOUT (same scheduler pass, right after accrual; POST /v1/admin/authors/:id/payout as an operator override) settles each author's pending royalty with NO human in the loop: "credits" issues a commerce grant into an external author's wallet; a Hanzo-MAINTAINED template's royalty is realized into the Hanzo treasury reserve instead ("pay ourselves"). A payout can never exceed pending (accrued − paid), guarded atomically — idempotent, never double-pays.

HANZO FORKS. A repo whose owner is a brand org (owner ∈ {hanzoai, hanzo-*}) is auto-attributed on first deploy to the treasury SYSTEM author (org = the brand slug), so Hanzo earns 20% on its OWN templates when other orgs deploy them, credited to the treasury reserve via the shared treasury client — no external wallet, no new ledger.

Surface:

GET  /v1/authors                       (org)          my status, login, verified, repos, deploys, accrued/pending/paid, payouts
GET  /v1/authors/basis                 (org)          why my number is my number: share, cost model, immutable rows, reconciliation
POST /v1/authors/connect               (org)          link GitHub (IAM-linked account or supplied login) + mint verify code
POST /v1/authors/repos/verify          (org)          verify repo ownership (oauth admin-check OR hanzo.json file)
POST /v1/authors/deploys/record        (org=deployer) record a deploy of a verified author repo (provenance → royalty)
GET  /v1/admin/authors                  (SuperAdmin) every author + a summary
POST /v1/admin/authors/sweep            (SuperAdmin) accrue royalty for every deploying org this period
POST /v1/admin/authors/:id/approve      (SuperAdmin) admit to earning (+ optional share override)
POST /v1/admin/authors/:id/suspend      (SuperAdmin) suspend
POST /v1/admin/authors/:id/payout       (SuperAdmin) record a payout (credits → grant; cash → record-only)
GET  /v1/admin/authors/:id/basis        (SuperAdmin) the SAME basis payload the author reads (support mirror)

serve.go auto-registers GET /v1/authors/health.

Index

Constants

View Source
const (
	ProviderGitHub = "github"
	ProviderGitLab = "gitlab"
)

Providers an author repo can live on. The provider is derived from the canonical repo host (github.com → github, gitlab.com → gitlab) so ONE code path serves both.

View Source
const (
	StatusConnected = "connected"
	StatusApproved  = "approved"
	StatusSuspended = "suspended"
)

Status values. An author advances connected → approved (and can be suspended). Only an APPROVED author accrues earnings; a connected author can still verify repos (ownership proof is independent of program admission).

View Source
const (
	MethodOAuth = "oauth"
	MethodFile  = "file"
	// MethodMaintainer = the repo is owned by the platform maintainer (owner ∈ the
	// brand's GitHub orgs, e.g. hanzoai / hanzo-*): ownership is intrinsic to the
	// namespace, so a Hanzo-maintained template is auto-verified under the treasury
	// SYSTEM author with no OAuth/file proof — its creator royalty accrues to the
	// Hanzo treasury ("pay ourselves") rather than an external author wallet.
	MethodMaintainer = "maintainer"
)

Verification methods for a repo. "oauth" = the caller's IAM-linked GitHub token proved admin permission on the repo; "file" = a hanzo.json on the repo's default branch contained the author's verify code (proves default-branch control).

Variables

This section is empty.

Functions

func AccrueForOrg

func AccrueForOrg(ctx context.Context, deployingOrg string, spend int64, period string, now int64) int

AccrueForOrg is the seam the unified affiliate accrual walk calls once per source org (with the spend it already read): it accrues royalty to EVERY approved author whose verified repo that org deployed (excluding the author's own org), latched at-most-once per (author, org, period). It resolves the mounted authors singleton; when authors is NOT mounted (a partial deploy, or an affiliates unit test that does not wire authors) it is a no-op returning 0 — the same degrade-gracefully contract treasury.Reserve uses. Returns the number of NEW royalty accruals latched.

func Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount wires the authors surface onto app per HIP-0106.

func Shutdown

func Shutdown() error

Shutdown stops the accrual+auto-payout scheduler (draining any in-flight sweep) and closes the authors store, in that order — so the store is never closed out from under a running payout. Idempotent.

Types

type Accrual

type Accrual struct {
	ID           string `json:"id"`
	AuthorID     string `json:"authorId"`
	DeployingOrg string `json:"deployingOrg"`
	Period       string `json:"period"`
	SpendCents   int64  `json:"spendCents"`
	EarningCents int64  `json:"earningCents"`
	CreatedAt    int64  `json:"createdAt"`
}

Accrual is one per-period royalty event: the deploying org's spend for that period × the author's share. UNIQUE(author, deploying_org, period) makes the sweep at-most-once per period — the royalty latch.

type Author

type Author struct {
	ID           string `json:"id"`
	Org          string `json:"-"` // the author's own org; admin view re-exposes it
	GithubLogin  string `json:"githubLogin"`
	VerifyCode   string `json:"-"` // per-author token for the hanzo.json file method
	Status       string `json:"status"`
	ShareBps     int64  `json:"shareBps"`
	AccruedCents int64  `json:"accruedCents"` // lifetime earnings accrued
	PaidCents    int64  `json:"paidCents"`    // lifetime earnings paid out
	CreatedAt    int64  `json:"createdAt"`
	VerifiedAt   int64  `json:"verifiedAt"` // GitHub identity OAuth-verified time (0 = file-only)
	ApprovedAt   int64  `json:"approvedAt"`
	SuspendedAt  int64  `json:"suspendedAt"`
}

Author is one OSS author org enrolled in the deploy-royalty program. Org is UNIQUE (one author per org). GithubLogin is the linked GitHub identity; VerifyCode is the stable per-author token placed in hanzo.json for the file method. ShareBps is the royalty rate in basis points (2000 = 20% of a deploying org's metered spend).

func (Author) PendingCents

func (a Author) PendingCents() int64

PendingCents is the royalty earned but not yet paid (never negative).

type AuthorOrg

type AuthorOrg struct {
	ID         string `json:"id"`
	AuthorID   string `json:"authorId"`
	OwnerURL   string `json:"ownerUrl"`
	Verified   bool   `json:"verified"`
	Method     string `json:"method"`
	CreatedAt  int64  `json:"createdAt"`
	VerifiedAt int64  `json:"verifiedAt"`
}

AuthorOrg is one whole OWNER/org an author has claimed (e.g. github.com/acme). A verified org claim COVERS every repo under that owner, so any repo the author publishes there earns without a per-repo verify. OwnerURL is the canonical "host/owner" form (UNIQUE across the table — an owner belongs to at most one author, first-verify wins). It sits beside AuthorRepo: a deploy matches a per-repo claim OR an owner-wide org claim (per-repo takes precedence).

type AuthorRepo

type AuthorRepo struct {
	ID         string `json:"id"`
	AuthorID   string `json:"authorId"`
	RepoURL    string `json:"repoUrl"`
	Verified   bool   `json:"verified"`
	Method     string `json:"method"`
	CreatedAt  int64  `json:"createdAt"`
	VerifiedAt int64  `json:"verifiedAt"`
}

AuthorRepo is one repository an author has claimed. RepoURL is the canonical host/owner/name form (UNIQUE across the table — a repo belongs to at most one author, first-verify wins). Verified flips true once ownership is proven.

type DeployEvent

type DeployEvent struct {
	ID           string `json:"id"`
	AuthorID     string `json:"authorId"`
	RepoURL      string `json:"repoUrl"`
	Project      string `json:"project"`
	DeployingOrg string `json:"deployingOrg"`
	CreatedAt    int64  `json:"createdAt"`
}

DeployEvent is one attribution edge: a deploying org deployed a project sourced from a verified author repo. UNIQUE(repo_url, project, deploying_org) makes the record idempotent (a re-deploy of the same project by the same org is one edge).

type LedgerRow

type LedgerRow struct {
	ID           string  `json:"id"`
	AuthorID     string  `json:"authorId"`
	DeployingOrg string  `json:"deployingOrg"`
	Period       string  `json:"period"`
	ShareBps     int64   `json:"shareBps"`
	SpendCents   int64   `json:"spendCents"`
	EarningCents int64   `json:"earningCents"`
	ComputeProof *string `json:"computeProof"` // nullable: hanzod attestation (follow-up)
	CreatedAt    int64   `json:"createdAt"`
}

LedgerRow is one immutable, on-chain-ready royalty record appended per latched accrual. ComputeProof is nil until a hanzod compute attestation binds the row (a follow-up) — it is never fabricated.

type Payout

type Payout struct {
	ID          string `json:"id"`
	AuthorID    string `json:"authorId"`
	AmountCents int64  `json:"amountCents"`
	Method      string `json:"method"`
	Reference   string `json:"reference"`
	Txn         string `json:"txn,omitempty"`
	// Settlement is where the money landed — settlementTreasury (internal: a
	// first-party author's royalty realized into our own reserve), settlementWallet
	// (a commerce grant to an external author), or settlementCash. Captured at
	// reservation, so the books say plainly which payouts were paid to ourselves.
	Settlement string `json:"settlement,omitempty"`
	CreatedAt  int64  `json:"createdAt"`
}

Payout is one recorded disbursement of accrued royalty. A "credits" method issues a commerce grant (Txn set); cash methods (wire/paypal/…) are record-only.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is the authors database. ONE SQLite file holds every org's author record, claimed repos, deploy-attribution edges, accrual events, and payouts. A repo→author lookup is a GLOBAL directory by design (any deploying org presents a repo minted by ANY author); every /v1/authors read is scoped by the caller's org server-side.

func (*Store) Approve

func (s *Store) Approve(ctx context.Context, id string, shareBps int64, now int64) (Author, error)

Approve admits an author to earning (status=approved) and optionally overrides the share rate. Idempotent. errNotFound if missing.

func (*Store) AuthorsDeployedBy

func (s *Store) AuthorsDeployedBy(ctx context.Context, deployingOrg string, limit int) ([]Author, error)

AuthorsDeployedBy returns the DISTINCT APPROVED authors whose VERIFIED repo the given org deployed, EXCLUDING any author whose own org is the deploying org (no self-royalty). This is the per-compute attribution set the unified accrual walk folds over for one source org.

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying database. Idempotent-safe via sql.DB.

func (*Store) Connect

func (s *Store) Connect(ctx context.Context, id, org, githubLogin, verifyCode string, shareBps int64, identityVerified bool, now int64) (Author, bool, error)

Connect enrolls org as an author at status=connected, idempotently. It records the GitHub login + verify code and, when identityVerified, stamps verified_at. A repeat connect UPDATES the login (re-link) but never resets status/accrual/verify_code — first connect owns the code. Returns (author, created).

func (*Store) DeployCountsByAuthor

func (s *Store) DeployCountsByAuthor(ctx context.Context) (map[string]int, error)

DeployCountsByAuthor returns author_id → deploy-event count in ONE GROUP BY.

func (*Store) DistinctDeployingOrgs

func (s *Store) DistinctDeployingOrgs(ctx context.Context, authorID, authorOrg string, limit int) ([]string, error)

DistinctDeployingOrgs returns the DISTINCT orgs that have deployed one of an author's repos, EXCLUDING the author's own org (no self-royalty). This is the accrual set — one accrual per (author, deploying_org, period).

func (*Store) EnsureSystemAuthor

func (s *Store) EnsureSystemAuthor(ctx context.Context, id, org, githubLogin string, shareBps, now int64) (Author, error)

EnsureSystemAuthor idempotently seeds a SYSTEM author (the Hanzo treasury "pay ourselves" identity) at status=approved, identity-verified, sharing shareBps. It owns the platform-maintained OSS templates so their creator royalty accrues to the treasury. A repeat call is a no-op that returns the existing row — never resetting its accrual, paid, or share. Returns the author keyed by org (org is UNIQUE, so one system author per treasury org).

func (*Store) GetByID

func (s *Store) GetByID(ctx context.Context, id string) (Author, error)

GetByID re-reads an author by id (post-mutation refresh).

func (*Store) GetByOrg

func (s *Store) GetByOrg(ctx context.Context, org string) (Author, error)

GetByOrg reads the author record for org, or errNotFound.

func (*Store) LatchAccrual

func (s *Store) LatchAccrual(ctx context.Context, accrualID, ledgerID, authorID, deployingOrg, period string, shareBps, spendCents, earningCents, now int64) (bool, error)

LatchAccrual atomically records ONE accrual for (author, deployingOrg, period), adds the earning to the author's accrued balance, AND appends the immutable royalty ledger row — all in a single transaction. A UNIQUE violation means this period was already accrued (returns false, no error, no double-accrual). Returns won=true only when THIS call created the accrual. ledgerID/shareBps stamp the ledger row; its compute_proof is written NULL (a hanzod attestation is a follow-up).

func (*Store) LedgerTotals

func (s *Store) LedgerTotals(ctx context.Context, authorID string) (rows int, earningCents int64, err error)

LedgerTotals foots the WHOLE ledger — every row, every period. The reconciliation it feeds must never be computed from a paged window, or a truncated read would silently claim the books balance.

func (*Store) ListAll

func (s *Store) ListAll(ctx context.Context, limit int) ([]Author, error)

ListAll returns every author newest-first (the admin directory), bounded.

func (*Store) ListApproved

func (s *Store) ListApproved(ctx context.Context, limit int) ([]Author, error)

ListApproved returns every approved author (the sweep set), oldest-first.

func (*Store) ListDeploys

func (s *Store) ListDeploys(ctx context.Context, authorID string, limit int) ([]DeployEvent, error)

ListDeploys returns an author's deploy events, newest-first, bounded.

func (*Store) ListLedger

func (s *Store) ListLedger(ctx context.Context, authorID, period string, limit int) ([]LedgerRow, error)

ListLedger returns an author's royalty ledger rows, newest-first, bounded. period narrows to ONE accrual bucket (the YYYY-MM periodKey mints); "" is every period.

func (*Store) ListOrgs

func (s *Store) ListOrgs(ctx context.Context, authorID string, limit int) ([]AuthorOrg, error)

ListOrgs returns an author's claimed owner-wide orgs, newest-first, bounded.

func (*Store) ListPayouts

func (s *Store) ListPayouts(ctx context.Context, authorID string, limit int) ([]Payout, error)

ListPayouts returns an author's payout history, newest-first, bounded.

func (*Store) ListRepos

func (s *Store) ListRepos(ctx context.Context, authorID string, limit int) ([]AuthorRepo, error)

ListRepos returns an author's claimed repos, newest-first, bounded.

func (*Store) RecordDeploy

func (s *Store) RecordDeploy(ctx context.Context, id, authorID, repoURL, project, deployingOrg string, now int64) (DeployEvent, bool, error)

RecordDeploy records a deploy-attribution edge, idempotently. UNIQUE(repo, project, deployingOrg) makes a re-deploy a no-op returning the FIRST edge. Returns (edge, created).

func (*Store) RecordPayout

func (s *Store) RecordPayout(ctx context.Context, payoutID, authorID string, amountCents int64, method, reference, settlement string, now int64) (Payout, error)

RecordPayout atomically RESERVES amountCents against the author's pending royalty (accrued − paid) and records a payout row — in one transaction. The WHERE guard makes it impossible to pay out more than is owed, even under concurrency (RowsAffected 0 → errInsufficientPending). The commerce grant (credits payout) happens AFTER, outside the tx; SetPayoutTxn records the receipt. errNotFound if the author is missing.

func (*Store) RepoCountsByAuthor

func (s *Store) RepoCountsByAuthor(ctx context.Context) (map[string]int, error)

RepoCountsByAuthor returns author_id → verified-repo count in ONE GROUP BY (the admin directory's per-row count, no N+1 fan-out).

func (*Store) SetPayoutTxn

func (s *Store) SetPayoutTxn(ctx context.Context, payoutID, txn string) error

SetPayoutTxn records the commerce ledger transaction id after a credits payout deposit lands (best-effort receipt; the pending reservation is the authority).

func (*Store) Suspend

func (s *Store) Suspend(ctx context.Context, id string, now int64) (Author, error)

Suspend moves an author to suspended (stops NEW accrual; earned royalty is unaffected). errNotFound if missing.

func (*Store) UpsertVerifiedOrg

func (s *Store) UpsertVerifiedOrg(ctx context.Context, id, authorID, ownerURL, method string, now int64) (AuthorOrg, bool, error)

UpsertVerifiedOrg records ownerURL as a VERIFIED owner-wide claim of authorID, idempotently. owner_url is UNIQUE: if it already belongs to THIS author the row is refreshed (method/verified_at updated); if it belongs to ANOTHER author the insert conflicts and ownership is refused via errOrgOwned. Mirrors UpsertVerifiedRepo — first-verify wins. Returns (org, created).

func (*Store) UpsertVerifiedRepo

func (s *Store) UpsertVerifiedRepo(ctx context.Context, id, authorID, repoURL, method string, now int64) (AuthorRepo, bool, error)

UpsertVerifiedRepo records repoURL as a VERIFIED repo of authorID, idempotently. repoURL is UNIQUE across the table: if it already belongs to THIS author the row is refreshed (method/verified_at updated); if it belongs to ANOTHER author the insert conflicts and errUnknownRepo-style ownership is refused via errRepoOwned. Returns (repo, created).

func (*Store) VerifiedOrgForURL

func (s *Store) VerifiedOrgForURL(ctx context.Context, repoURL string) (AuthorOrg, error)

VerifiedOrgForURL resolves a canonical REPO url to a VERIFIED owner-wide claim that COVERS it: it derives the repo's "host/owner" and looks up an author_orgs row. This is the org arm of deploy attribution — a repo with no per-repo claim still earns when its owner is a verified org. Returns errUnknownRepo (no claim) / errRepoNotVerified (claim exists but unverified), matching VerifiedRepoForURL so the deploy path treats both arms uniformly.

func (*Store) VerifiedRepoForURL

func (s *Store) VerifiedRepoForURL(ctx context.Context, repoURL string) (AuthorRepo, error)

VerifiedRepoForURL resolves a canonical repo url to its VERIFIED owning author + repo, or errUnknownRepo / errRepoNotVerified. The deploy-record attribution path.

func (*Store) VoidPayout

func (s *Store) VoidPayout(ctx context.Context, payoutID, authorID string, amountCents int64) error

VoidPayout reverses a RecordPayout that could not be BACKED by the treasury reserve: it deletes the payout row and restores the reserved amount to pending (paid_cents −= amount), in one transaction. It is the compensating action when the fund cannot cover a payout the pending-guard already reserved — so a blocked payout leaves the author's pending royalty intact, honestly, instead of silently burning it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL