authors

package
v1.801.63 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Overview

Package authors mounts the Hanzo Cloud /v1/authors/* OSS-author surface: a native-Go, per-org program on Base/SQLite that pays open-source AUTHORS a royalty on the metered platform spend of the orgs who DEPLOY their projects on Hanzo. It sits next to clients/referrals (a one-time credit for both sides) and clients/affiliates (an ongoing partner commission on referred customers) as the THIRD growth loop — the CREATOR one — and mirrors their structure exactly: one SQLite store, server-side tenant isolation, one Mount, HIP-0106, and the SAME 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 (POST /v1/admin/authors/sweep, the cron path; 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 (default 5%), accrued at-most-once per (author, deploying_org, period).
  5. Staff PAY OUT accrued royalty (POST /v1/admin/authors/:id/payout): "credits" issues a commerce grant into the author's wallet; cash methods are record-only. A payout can never exceed pending (accrued − paid), guarded atomically.

Surface:

GET  /v1/authors                       (org)          my status, login, verified, repos, deploys, accrued/pending/paid, payouts
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)

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"
)

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 added in v1.800.1

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 *zip.App, deps cloud.Deps) error

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

func Shutdown

func Shutdown() error

Shutdown closes the authors store. 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 (500 = 5% 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 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 added in v1.800.1

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"`
	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 added in v1.800.1

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) 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) 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 added in v1.800.1

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

ListLedger returns an author's royalty ledger rows, 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 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) 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) 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