captable

package
v1.801.388 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: 14 Imported by: 0

Documentation

Overview

Package captable is your cap table: stakeholders, share classes, grants, SAFEs, rounds, and who owns what.

In full: stakeholders, share classes, share certificates and transfers, option grants and equity plans, SAFEs and convertible notes, priced rounds and their investments, and the summary that totals outstanding and fully-diluted ownership from them. It runs per tenant on Base/SQLite in the unified cloud binary (HIP-0106).

WRAP, DON'T REWRITE — the read-WRITE variant. Where apps/plan + apps/pricing host a read-only @hanzo catalog in goja, captable hosts the tRPC business LOGIC (ported to a self-contained goja bundle in github.com/hanzoai/ captable) and gives it PERSISTENCE over per-tenant Base/SQLite. The bundle carries logic; the Go host carries storage. The seam between them is the REUSABLE apps/goja binding (the RW-Base goja host), which esign (#100) and dataroom (#101) reuse unchanged — this leaf is just:

captable bundle (github.com/hanzoai/captable.Bundle)  +  the per-tenant Schema
                         │
                  apps/goja.NewBase(...)   ← injects __db/__newId/__now,
                         │                       one SQLite file per tenant,
                  /v1/captable/* zip routes     one transaction per request

No Prisma, no Postgres, no Next.js in this path. Every route resolves the org from the VALIDATED cloud principal (principal.Org), never a client header, and that org selects the tenant's DB file AND scopes every row.

ACTIVATION: captable is NOT staged — it mounts under the mount-all default (empty CLOUD_ENABLE), so the one binary serves /v1/captable/* from first boot. There is no standalone Captable,Inc pod to defer to (the Next.js/Prisma/Postgres app is retired by this fold — no such deployment runs in the fleet), so cloud's fresh per-tenant Base/SQLite is authoritative from the first write, with no data to migrate.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotMounted = errors.New("captable: subsystem not mounted")

ErrNotMounted is returned when captable has not been mounted. A caller treats it as "cap table unavailable".

Functions

func AddStakeholders

func AddStakeholders(ctx context.Context, org string, holders []StakeholderInput) (int, error)

AddStakeholders adds one or more stakeholders (dedup by email in the bundle) and returns how many rows were inserted.

func EnsureShareClass

func EnsureShareClass(ctx context.Context, org string, in ShareClassInput) (string, error)

EnsureShareClass creates a share class and returns its id (resolved via the list route, since shareClasses.create does not echo the id). If a class with the same name already exists it returns that existing id (idempotent seed).

func IssueShares

func IssueShares(ctx context.Context, org string, in ShareInput) error

IssueShares issues a share certificate to a stakeholder.

func Mount

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

Mount wires the /v1/captable/* surface onto app per HIP-0106. Constructs the value directly (cloud.NewBase) — this subsystem keeps a package global for the Shutdown hook and opens a per-tenant goja host from deps.DataDir.

func RecordRound

func RecordRound(ctx context.Context, org string, in RoundInput) (string, error)

RecordRound records a fundraising round and returns its id.

func SetIncorporation

func SetIncorporation(ctx context.Context, org, companyName, incType, country, state string) error

SetIncorporation records the entity kind on the tenant's canonical company row — the cap-table-layer "org is now a company" fact. companyName is required by the bundle's company.update contract.

func Shutdown

func Shutdown(context.Context) error

shutdown closes the per-tenant stores + the goja engine. Idempotent.

func StakeholderIDsByEmail

func StakeholderIDsByEmail(ctx context.Context, org string) (map[string]string, error)

StakeholderIDsByEmail lists the tenant's stakeholders and returns an email→id map, so a caller that just added founders can resolve their ids to issue shares.

Types

type RoundInput

type RoundInput struct {
	Name              string  `json:"name"`
	RoundType         string  `json:"roundType"` // PRICED | SAFE | CONVERTIBLE_NOTE
	TargetAmount      float64 `json:"targetAmount"`
	PreMoneyValuation float64 `json:"preMoneyValuation,omitempty"`
	PricePerShare     float64 `json:"pricePerShare,omitempty"`
	ShareClassID      string  `json:"shareClassId,omitempty"`
}

RoundInput mirrors the rounds.create bundle contract.

type ShareClassInput

type ShareClassInput struct {
	Name                          string  `json:"name"`
	ClassType                     string  `json:"classType"` // COMMON | PREFERRED
	InitialSharesAuthorized       int64   `json:"initialSharesAuthorized"`
	VotesPerShare                 int     `json:"votesPerShare"`
	ParValue                      float64 `json:"parValue"`
	PricePerShare                 float64 `json:"pricePerShare"`
	Seniority                     int     `json:"seniority"`
	ConversionRights              string  `json:"conversionRights"`
	BoardApprovalDate             string  `json:"boardApprovalDate"`
	StockholderApprovalDate       string  `json:"stockholderApprovalDate"`
	LiquidationPreferenceMultiple float64 `json:"liquidationPreferenceMultiple"`
	ParticipationCapMultiple      float64 `json:"participationCapMultiple"`
}

ShareClassInput mirrors the shareClasses.create bundle contract.

type ShareInput

type ShareInput struct {
	StakeholderID     string `json:"stakeholderId"`
	ShareClassID      string `json:"shareClassId"`
	CertificateID     string `json:"certificateId"`
	Quantity          int64  `json:"quantity"`
	Status            string `json:"status"`    // ACTIVE | DRAFT
	IssueDate         string `json:"issueDate"` // ISO date
	CliffYears        int    `json:"cliffYears"`
	VestingYears      int    `json:"vestingYears"`
	BoardApprovalDate string `json:"boardApprovalDate"`
}

ShareInput mirrors the shares.add bundle contract.

type StakeholderInput

type StakeholderInput struct {
	Name                string `json:"name"`
	Email               string `json:"email"`
	StakeholderType     string `json:"stakeholderType"`     // INDIVIDUAL | INSTITUTION
	CurrentRelationship string `json:"currentRelationship"` // FOUNDER | INVESTOR | EMPLOYEE …
	InstitutionName     string `json:"institutionName,omitempty"`
}

StakeholderInput mirrors the stakeholders.add bundle contract.

Jump to

Keyboard shortcuts

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