HEARTH Tier Model & ProvisioningProfile
rocky intermediate 5 min read
ELI5
A tier (solo, team, studio, bespoke) is a name in YAML; what it actually means is a ProvisioningProfile — the resource caps and driver-specific flags that the same four-verb driver reads. Adding a new tier never adds a new code path, only a new YAML row.
Technical Deep Dive
The tier table (system-redesign §Tiers)
| Tier | Audience | CAIRNET shape | LORE shape | Polar gate |
|---|---|---|---|---|
solo | OSS self-host, single dev | SQLite + local FAISS, 100 MB cap | single-user, 30-day retention | none — always free |
team | small team, hosted | Postgres + pgvector, 5 GB cap, 5 seats | multi-user, 90-day retention, RBAC | rocky-team subscription |
studio | larger orgs, hosted | Postgres + pgvector, 100 GB cap, unlimited seats | multi-user, 1y retention, audit export | rocky-studio subscription |
bespoke | per-deal | driver-defined | driver-defined | out-of-band Polar invoice |
Adding a tier never adds code paths. Tier ≠ code path; drivers ≠ tiers (decision in system-redesign §Big design decisions).
ProvisioningProfile shape (Phase 5 §6)
{ tier: "solo" | "team" | "studio" | "bespoke", resource_caps: { cairnet_storage_mb: number, lore_retention_days: number, seats: number, vector_index: "faiss-local" | "pgvector" }, driver_flags: Record<string, unknown> // explicit per-driver extension point}KILN-extensibility: every nested object accepts .passthrough() so unknown fields ride through without schema bumps. driver_flags is the explicit per-driver extension; resource_caps is the cross-driver cap surface.
Class diagram
classDiagram class ProvisioningProfile { +Tier tier +ResourceCaps resource_caps +Record~string,unknown~ driver_flags } class ResourceCaps { +number cairnet_storage_mb +number lore_retention_days +number seats +VectorIndex vector_index } class Tier { <<enum>> solo team studio bespoke } class VectorIndex { <<enum>> faiss-local pgvector } class DriverName { <<enum>> local-docker kustomize devarno-cloud } ProvisioningProfile --> Tier ProvisioningProfile --> ResourceCaps ResourceCaps --> VectorIndex ProvisioningProfile ..> DriverName : selected by envHow a tier resolves to a profile
The HEARTH server reads tier → ProvisioningProfile from a YAML row (one per tier). Phase 5 ships only the LocalDocker driver, but every tier resolves to a profile that any driver can consume. The driver picks the bits it cares about and treats unknown driver_flags as opaque.
Why solo is special
solo is the OSS-parity floor. The cloud build’s Polar gate is a no-op for solo because it is “always free”; the self-host build forces tier=solo when ROCKY_BILLING=disabled (rocky-012). The full e2e test must pass against solo + LocalDocker + LocalAuth with no cloud credentials.
Key Terms
- Tier → a YAML-resolvable name;
solo/team/studio/bespoke ProvisioningProfile→ the structured payload a driver reads; resource caps + driver flagsdriver_flags→ explicit per-driver extension point (Record<string, unknown>)- KILN-extensibility →
.passthrough()lets unknown fields ride through without a schema bump
Q&A
Q: What changes when a new tier is added?
A: Only YAML — a new Tier enum value plus the corresponding ProvisioningProfile row. No driver code changes; no console code changes; no schema bump unless the cap shape itself changes.
Q: Why is vector_index on resource_caps rather than driver_flags?
A: It is a cross-driver cap (every CAIRNET deployment picks one), not a driver-specific tweak. driver_flags is for things only LocalDocker or only DevarnoCloud would understand.
Q: Does bespoke need code?
A: No — its profile is driver-defined and driver_flags carries the bespoke knobs. Per-deal customisation rides the existing extension surface; the Polar invoice is out-of-band.
Examples
A gym with four memberships (solo, team, studio, bespoke). The membership card says nothing about which weights you can lift — that lives in a printed schedule (ProvisioningProfile). The same trainer (driver) reads the schedule and decides what to set up, regardless of which card you flashed at the door.
neighbors on the map
- Billing Architecture debugging why a user's tier did not update after payment
- Site Hosting Modes & Lifecycle Stages adding a new fork branching on platform vs user_git
- LORE+CAIRNET Data Model Isolation understanding why LORE and CAIRNET data are separate