An audit read the code and found the proposal that specifies it. Go reaches
all 122 capability proposals and 64 of the 121 other Standards Track ones;
Rust has 35, mostly the operator and the CLI; C++ has three, because the
complete original C++ in the estate is a ZAP handler, a bot nothing specifies,
and authored kernels inside forked CUDA and Metal crates.
| | shipped | partial | none | not assessed |
| Go | 146 | 33 | 1 | 80 |
| C++ | 1 | 2 | 0 | 257 |
| Rust | 10 | 25 | 1 | 224 |
The large "not assessed" columns are the point rather than a gap. The model and
training specs are Python, the frontend and design-system proposals are
TypeScript, and recording `none` against those would read as a search that
came up empty when the honest statement is that the runtime was never the
question. Governance, Meta and Process are not graded at all.
Six proposals are Final with nothing found in Go and are left unwritten rather
than demoted: 0065, 0089, 0098, 0103, 0521 and 0902. HIP-0521 answers itself,
its own section reading "Not yet implemented". Three of the others name a
canonical Go file in consensus/protocol/auth that nobody wrote, in a package
where the four neighbouring proposals' files all exist with tests beside them.
Co-authored-by: Hanzo Dev <[email protected]>
5.0 KiB
hip, title, author, type, category, capability, status, implementation-go, created, requires
| hip | title | author | type | category | capability | status | implementation-go | created | requires |
|---|---|---|---|---|---|---|---|---|---|
| 1141 | Pref — One Document Per Person | Hanzo AI | Standards Track | Interface | pref | Final | shipped | 2026-08-20 | HIP-0026, HIP-0106, HIP-0139 |
HIP-1141: Pref — One Document Per Person
Abstract
/v1/pref is the signed-in user's own UI state — theme, density, pinned nav —
one JSON document per person, following them across every Hanzo surface. It is
implemented in hanzoai/cloud at apps/pref. This HIP states the two facts
that define it: the tenancy key is the USER, not the org, and the write is a
shallow merge so no surface can delete another surface's keys.
Motivation
Each app keeping its own copy of a person's settings in its own localStorage
makes the same person look like two different users depending on which tab they
are in (apps/pref/prefs.go:7-11). One store behind the user menu ends that.
It is deliberately NOT settings: settings is per-org, per-product
configuration with KMS custody for secret fields; collapsing the two would put
one user's theme under an org key and make an org admin the owner of everyone's
UI (apps/pref/prefs.go:31-35).
Specification
The key words MUST, MUST NOT and SHOULD are to be interpreted as in RFC 2119.
The store
One system-namespace SQLite file, prefs.db, opened through sqlpool.Open —
born encrypted under the cek-derived key, single-connection
(apps/pref/store.go:47-58). One row per subject, holding an opaque JSON
document the server bounds but never interprets. It MUST hold no secret: a
preference that needs custody does not belong in this table.
Addresses
Two operations at one path:
GET /v1/pref— a typed op. A caller who has never saved anything gets an empty document at 200, never a 404, so the user menu always renders.PATCH /v1/pref— an untyped handler declared with prose (apps/pref/prefs.go:115-128), because three of its wire facts are unreachable from a typed op (apps/pref/prefs.go:148-165): a 16 KiB request-byte cap answering 413, an empty or literal-nullbody answering 400 where zip's decode would make both a successful no-op, and an OPEN key space whose only carrier —map[string]any— publishes no request body at all.
PATCH, not PUT: a surface saves the keys it owns without having to send back
keys it does not know about. The merge is shallow, a named key is replaced
whole, a null value deletes its key, and the merge runs inside one store
transaction so two tabs saving different keys both survive
(apps/pref/store.go:93-101). A document over 16 KiB or 128 keys is refused.
Tenancy
The subject is the canonical <owner>/<name> identity built from values the
identity boundary minted from a validated credential (HIP-0026), and it is the
mandatory predicate on every store statement (apps/pref/prefs.go:202-215).
The bare user name is not unique across orgs — hanzo/z and admin/z are two
people — which is why the org qualifies the key. There is deliberately NO path
to another user's preferences: not for an org admin, not for a platform
SuperAdmin, because no operational task requires reading someone's theme. An
unvalidated principal is 403.
Money, events, telemetry
Free, said in those words: plugin/pref/main.go declares cloud.Free and the
app appears in no metered list. It publishes nothing to the bus, so a
customer's webhooks receive nothing from it. It emits nothing beyond the
request span every route gets.
Stage
ga: the personal half of the identity core — every signed-in surface reads
it — and its manifest row (manifest/apps.go:362) declares no stage.
Upstream
Derives from none. The store is the platform's own encrypted SQLite through the one shared opener; nothing external is forked, embedded or mirrored.
Rationale
The alternative tenancy key is the org, which every sibling store uses, and it is wrong here in a way that matters: preferences are personal, and an org key makes the org's admin their reader. Keying on the qualified user and refusing every cross-read — rather than gating cross-reads behind admin-ness — removes the check that could be forgotten. The alternative to server-side merge is client-side read-modify-write, which is a lost update the moment two tabs save different keys.
Security Considerations
The wrong implementation is an identity fold. Key on the bare user name and two
users named z in different orgs read and overwrite each other's documents —
which is why the subject is <owner>/<name> and both halves are length-bounded
before use, so an oversized forged header cannot become a giant primary key
(apps/pref/prefs.go:199-214). The document bound is the other exposure: an
unbounded personal, unaudited row becomes free general-purpose storage, so the
16 KiB / 128-key caps are enforced on the raw bytes before any parse.
References
- HIP-0026 — Identity and Access Management
- HIP-0106 — The Hanzo Plugin Contract
- HIP-0139 — Capability
Copyright
Released under CC0 1.0 Universal Public Domain Dedication.