HIP-1312: Company — The Formation Machine
Abstract
/v1/company brings a legal entity into existence and records who owns it:
choose a structure, add founders, clear identity, pay the formation fee,
generate documents, route signature, anchor the equity genesis. The
implementation is hanzoai/cloud apps/company.
The capability is a guarded state machine — a closed table of stages and the
edges between them (apps/company/machine.go) — plus provider seams that reach
the outside world (apps/company/providers.go). HIP-0903 argues why a firm
should run this way; this is the contract the surface answers to.
Motivation
Formation is a sequence where every step depends on a fact an earlier one established, and most of those facts are legal rather than technical: money must not move before identity clears, and an entity must not be reported as formed before it is.
Enumerating what a caller may do — a permission list — fails in the direction that matters here. A list has gaps, and a gap is a company incorporated without a verified person behind it. A closed edge table has none: the illegal move is not forbidden, it is absent.
Specification
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.
§1 The machine
Two paths reach the terminal stage, every edge carrying a guard
(apps/company/machine.go): incorporate — structure → founders → payment → documents → esign → genesis → company, guarded by structure chosen, identity
verified, fee paid, documents generated, signature complete, genesis recorded;
and import — structure → import → company, for an entity that already
exists and brings its own cap table and documents.
The table MUST stay closed: an edge that is not listed does not exist, so no request shape, argument or ordering advances a formation past a guard. A new step is a new edge with a new guard in the table, never a condition in a handler.
§2 The addresses
Twenty-one paths, every one under /v1/company (manifest/apps.go:433,
plugin/company/openapi.json): the machine's edges (/structure, /founders,
/kyc, /kyc/decision, /kyc/refresh, /payment, /documents, /esign,
/esign/complete, /genesis, /advance, /skip), the import pair, the
fundraise trio, the platform book (/register, /register/summary, /review)
and the GET|POST /v1/company root.
Twenty operations are typed. Two are declared with prose beside the route and
held to that count by a test (apps/company/typed_wire_test.go): the deck
upload takes a PDF as the raw request body, which has no value to name, and
POST /payment answers denial with the fleet's nested error body, which a typed
operation cannot emit. Both declare their bodies through openapi.Register, so
neither publishes an address nobody can explain.
§3 The store, and the tenant
One SQLite file in the system namespace — company — holds each org's formation
as a single row keyed by the org, at most one formation per org
(apps/company/store.go:22-28). Documents, cap-table rows and signing material
are NOT here: they live with dataroom, captable and kms behind the
provider seams (apps/company/providers.go), and a formation row that also held
them would make this capability the second owner of three other stores.
The org is principal.Org, the validated IAM owner claim (HIP-0026), on every
read and write; a caller with no validated principal is refused.
Two surfaces are SuperAdmin's and cross-tenant by nature: the platform book
(/register, /register/summary, /review) and the identity decision
(/kyc/decision). Hanzo forms the entity, so Hanzo carries the formation
identity obligation, and discharging it is the platform's own act rather than a
predicate over the customer's. The two planes MUST stay orthogonal — the
SuperAdmin operation writes a fact, the machine's guard reads it, neither calls
the other — and a reviewer's confirmation MUST be recorded as its own distinct
value, never as a provider verification. "A human confirmed this" and "a
provider verified this" stay different facts to an auditor forever. Every
privileged decision carries the deciding reviewer.
§4 The genesis anchor
The founding allocation — entity plus founders, ordered deterministically — is
hashed to a keccak root and committed to the Hanzo L1 (chain 36963) by a
KMS-signed transaction (apps/company/genesis.go). The chain is the source of
truth, so a holder of the allocation recomputes the root without trusting this
platform. When the RPC or the signer is unconfigured, the root MUST be returned
with an honest pending status: a transaction hash is never fabricated and
formation is never blocked on an unreachable chain.
§5 Price, events, emission, stage, upstream
The capability is metered: Price: cloud.Metered
(plugin/company/main.go:27; spend.go:294) — the surface moves a four-figure
sum, so the edge requires standing. The one charge is the one-time formation fee —
99900 cents (formationFeeCents, apps/company/providers.go:146; operator
override CLOUD_COMPANY_FEE_CENTS, apps/company/company.go:290-297) — taken
at POST /v1/company/payment through the charge seam onto the org's own ledger.
Insufficient balance is 402 and an unreachable balance is 503; neither advances
the stage, because the paid guard reads the receipt rather than the attempt
(apps/company/machine.go:332-335).
It publishes no events on the bus, so a customer's webhooks (HIP-1310) receive
nothing from it — provider completion arrives INBOUND at /esign/complete and
/kyc/decision, it is not emitted. It emits nothing to observability beyond the
request span every route gets.
The stage is beta (HIP-0139 §8): a vertical application rather than core, so
an org reaches it by the company flag. The manifest row declares it
(manifest/apps.go:433, Stage: Beta). It derives from no OSS upstream — the
identity and filing providers are hand-written clients behind the seams
(apps/company/providers.go, apps/company/filing.go).
Rationale
The alternative to the closed edge table is scopes: enumerate what each caller may invoke and check the enumeration in every handler. It costs one gap to be wrong, the gaps are invisible until exploited, and each new stage multiplies the checks that must agree. The table has one place to be right.
The alternative to the platform book is per-tenant records only, which cannot answer what the obligation actually poses: how many formations await review, and which have waited longest. That question is cross-tenant because the duty is.
Security Considerations
The wrong implementation hands an attacker one of three things. Another org's formation row is its founders' identity documents and personal data — the most sensitive record this platform holds outside secrets. A decision operation reachable by a non-SuperAdmin is laundered identity approval: a self-declared founder who then clears the guard. And an edge table with one extra entry is a company formed without payment or without a verified person behind it — a real entity, in a real jurisdiction, traceable to us. The anchor adds a fourth: a fabricated transaction hash is a false claim of public verifiability, worse than no claim, because a reader who checks the chain and finds nothing has already relied on it.
References
- HIP-0026 — Identity and Access Management
- HIP-0106 — Hanzo Plugin Contract
- HIP-0139 — Capability
- HIP-0903 — The Agentic Company — Autonomous Firms on Hanzo
- HIP-1310 — Webhooks — Outbound Delivery
Copyright
Released under CC0 1.0 Universal Public Domain Dedication.