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:
- 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.
- 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.
- 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.
- 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).
- 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
- func AccrueForOrg(ctx context.Context, deployingOrg string, spend int64, period string, ...) int
- func Mount(app *zip.App, deps cloud.Deps) error
- func Shutdown() error
- type Accrual
- type Author
- type AuthorRepo
- type DeployEvent
- type LedgerRow
- type Payout
- type Store
- func (s *Store) Approve(ctx context.Context, id string, shareBps int64, now int64) (Author, error)
- func (s *Store) AuthorsDeployedBy(ctx context.Context, deployingOrg string, limit int) ([]Author, error)
- func (s *Store) Close() error
- func (s *Store) Connect(ctx context.Context, id, org, githubLogin, verifyCode string, shareBps int64, ...) (Author, bool, error)
- func (s *Store) DeployCountsByAuthor(ctx context.Context) (map[string]int, error)
- func (s *Store) DistinctDeployingOrgs(ctx context.Context, authorID, authorOrg string, limit int) ([]string, error)
- func (s *Store) GetByID(ctx context.Context, id string) (Author, error)
- func (s *Store) GetByOrg(ctx context.Context, org string) (Author, error)
- func (s *Store) LatchAccrual(ctx context.Context, ...) (bool, error)
- func (s *Store) ListAll(ctx context.Context, limit int) ([]Author, error)
- func (s *Store) ListApproved(ctx context.Context, limit int) ([]Author, error)
- func (s *Store) ListDeploys(ctx context.Context, authorID string, limit int) ([]DeployEvent, error)
- func (s *Store) ListLedger(ctx context.Context, authorID string, limit int) ([]LedgerRow, error)
- func (s *Store) ListPayouts(ctx context.Context, authorID string, limit int) ([]Payout, error)
- func (s *Store) ListRepos(ctx context.Context, authorID string, limit int) ([]AuthorRepo, error)
- func (s *Store) RecordDeploy(ctx context.Context, id, authorID, repoURL, project, deployingOrg string, ...) (DeployEvent, bool, error)
- func (s *Store) RecordPayout(ctx context.Context, payoutID, authorID string, amountCents int64, ...) (Payout, error)
- func (s *Store) RepoCountsByAuthor(ctx context.Context) (map[string]int, error)
- func (s *Store) SetPayoutTxn(ctx context.Context, payoutID, txn string) error
- func (s *Store) Suspend(ctx context.Context, id string, now int64) (Author, error)
- func (s *Store) UpsertVerifiedRepo(ctx context.Context, id, authorID, repoURL, method string, now int64) (AuthorRepo, bool, error)
- func (s *Store) VerifiedRepoForURL(ctx context.Context, repoURL string) (AuthorRepo, error)
- func (s *Store) VoidPayout(ctx context.Context, payoutID, authorID string, amountCents int64) error
Constants ¶
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.
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).
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.
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"`
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 ¶
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"`
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 ¶
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) 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 ¶
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) 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) ListApproved ¶
ListApproved returns every approved author (the sweep set), oldest-first.
func (*Store) ListDeploys ¶
ListDeploys returns an author's deploy events, newest-first, bounded.
func (*Store) ListLedger ¶ added in v1.800.1
ListLedger returns an author's royalty ledger rows, newest-first, bounded.
func (*Store) ListPayouts ¶
ListPayouts returns an author's payout history, 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 ¶
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 ¶
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 ¶
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 ¶
VerifiedRepoForURL resolves a canonical repo url to its VERIFIED owning author + repo, or errUnknownRepo / errRepoNotVerified. The deploy-record attribution path.
func (*Store) VoidPayout ¶
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.