Status: Proposed (design-panel synthesis) ·
Date: 2026-07-22 · Track: E8 (premium
vetted community — Pillar 1 tier + admission) Method:
adversarial design panel — estate-scout + four expert lenses
(payments-security · compliance & legal · data · simplicity).
Dissent preserved below.
Follows the ADR-001 template. Brand-free: processors/tools named by
capability only. This ADR is the direct extension of ADR-001's I9/I10
into the billing dimension.
1. Context
The foundation spec (§2, §7) locks four tiers as a single
attribute on the member record, read at every surface and at AI
retrieval, re-implemented nowhere. E8 must make that attribute
earn-able and pay-able: a payments processor to take fiat, an
entitlements model that turns "who paid / who was admitted" into "what
this member can reach on every surface," and a vetting/admission
workflow (Guest→Member self-serve; Member→Inner-Circle vetted; Founding
granted; grandfather the 60).
The estate-scout confirmed the load-bearing gap: there is no
payment processor, no subscription lifecycle, no checkout, no
webhook→entitlement sync, and no member/tier billing model anywhere in
the estate. The fiat side is ~80% greenfield. What is
reusable is a pattern, not code: the capability-gate's
fail-closed, default-deny
evaluate → (allowed, reason, audit-event) contract maps
almost 1:1 onto a tier/feature entitlement check, and the agent-wallet's
windowed-limit + kill-switch + immutable JSONL audit shape is a
serviceable template for usage quotas and the entitlement-transition
audit trail. Both are agent-facing or crypto-rail — the contract
transfers, the code does not. So E8 is a genuine build, anchored on a
payment integration the estate does not have, sitting on top of
the tier-at-read substrate ADR-001 already decided.
2. Decision drivers
- Tier is the sole permission authority. Payments
feed the tier attribute; a processor or platform must never
own access (a forked permission authority is exploitable for
free paid-access and un-revoked chargebacks — the money-attached version
of ADR-001's two-permission-systems failure).
- Entitlement must be revocable at read. A dispute,
refund, or failed renewal is an attribute downgrade that de-provisions
every surface (and the AI retrieval filter) at the next read. Nothing
caches "paid" into a token, session, feed, or index chunk.
- Small team: don't hand-roll a billing engine. Rent
proration, dunning, tax, and invoicing from the processor's native
subscription primitives; own only the entitlement event and the
tier attribute.
- PCI blast radius is a first-class design
constraint. Card data never touches our iron — hosted checkout
keeps us at SAQ-A; the shell renders chrome around the
processor's fields, never our own PAN inputs.
- Reversibility. A processor swap must be a repoint
of one
subscription→tier function; the entitlement
authority is irreversible.
- Compliance is load-bearing, not a footnote. Owning
tier means owning the liability story by construction — the compliance
work is the price of owning the moat, not extra.
3. Options considered
- A — Processor direct + own entitlements. Hosted
checkout; tier→access lives in our own member record, driven by
processor webhooks; vetting/admission is our own flow.
- B — Bundled course/commerce platform. One vendor
ships payments + subscriptions + course-selling + entitlements; tier
becomes their "product access" concept.
- C — Processor + estate entitlement patterns. Same
as A, but entitlement enforcement borrows the estate's fail-closed
capability-gate/tier-gated-API contract rather than inventing new
gating; the processor is the only genuinely new component.
- D — Subscription/marketplace layer. A
billing/marketplace middleware owns plans, proration, dunning, tax;
entitlements sync back to us from it.
4.
The panel (rankings diverged; read the core argument, not the
rank)
| Payments-security (webhook integrity · PCI ·
chargeback abuse) |
C |
The decisive question is "is the payment system allowed to
own entitlement, or only to emit signed events we turn
into entitlement." Processor = signed event source only: verify
signature → dedup on event-id → append immutable entitlement event →
recompute tier. Hosted checkout keeps SAQ-A; dispute→revocation must be
automatic; secrets in SOPS, signing-secret separate from API key. |
| Compliance & legal (GDPR/CCPA · ToS/liability ·
portability) |
A |
Clean liability split: processor owns payment PII/chargebacks under
its DPA; we own tier + admission, so the liability
story is ours by construction. Vetting data is "legitimate-interest" PII
with a hard retention limit + Art.15/17 rights. Refund policy (T0→T1
window; T1→T2 rejection keeps T1) must be unambiguous pre-purchase. You
cannot borrow your way out — pick the architecture, pay the compliance
tax. |
| Data (Kleppmann) |
A |
The processor is the truth for money, never for
access. Webhooks are at-least-once, out-of-order, lossy — a
hint, not a command. Entitlement is a read-time
function over the append-only admission/billing-event log + a
rebuildable subscription-status projection, reconciled by a
periodic pull (the load-bearing half). Out-of-order
delivery ordered by per-member effective_at. |
| Simplicity (Torvalds) |
A |
The entitlements model already exists:
object.tier_visibility ≤ member.tier, evaluated at read
(I9). E8 adds zero entitlement code — just a webhook
that writes the attribute. Ship one paid tier
(Guest→Member); the 60 are a column write; vetting/admission machinery
is built when the first Member asks to upgrade, shaped around a
real applicant. Keep the 4-value enum (a cheap ordering), defer the
machine. |
| Estate-scout |
— |
The fiat side is confirmed greenfield (~80% new). Real reuse (~20%):
the capability-gate's fail-closed
evaluate → (allowed, reason, audit-event) contract as the
entitlement check, and the wallet's windowed-limit/kill-switch + JSONL
audit shape for quotas and the transition trail. Nothing ships as-is; no
code copies cleanly. |
(Note: the data lens numbered its ballot 1–4 as Option-1…4;
mapped here A/C/B/D. It, compliance, and simplicity all rank A #1;
payments-security ranks C #1. Both agree A≈C. Every lens ranks the
vendor-owns-access options (B, D) last.)
5.
The convergence (what all five agree on, under the different ranks)
Despite the C-vs-A split at the top, and B/D fighting for last, the
lenses converge on one architecture:
- The processor is the source of truth for money,
never for access. Payments-security ("signed event
source only"), Kleppmann ("truth for money, not access"), Torvalds
("rent the checkout, never rent the answer to is this member
allowed"), Compliance ("processor owns payment PII, we own tier")
enter from four doors into the same room. A vendor holding the
entitlement authority (B, D) is the ADR-001 fork+reskin failure with
money attached — two permission systems that drift, a chargeback that
leaves their record stale vs our tier attribute, a
direct violation of I10.
- A≈C; the top-of-ballot split is cosmetic. A and C
are the same architecture — the only difference is whether
entitlement enforcement reuses the estate's fail-closed
capability-gate contract (C) or restates it (A). The reuse is a
contract, not Ruby code (the gate is in-process, config-static,
agent-facing). So the decision adopts A's shape and C's
contract.
- Webhooks are events, not commands.
Signature-verified, replay-resistant (event-id dedup + timestamp
window), idempotent, appended to an immutable entitlement log — then
we recompute tier. A forged/replayed
payment.succeeded must not mint free access; a
missed chargeback must not leave a fraudster
inside with retrieval access.
- Entitlement is derived and read-time; nothing caches
"paid." A dispute/refund/lapse is a single attribute downgrade;
tier-at-read (I9) de-provisions every surface and the AI filter at the
next read. No tier claim outlives a chargeback in a
token/session/feed/index chunk.
- Ship one paid tier first; the 4-tier machinery is data, not
machinery. Because tier is one ordered attribute and
entitlement is a read-time function, T2/T3 are new tier values + new
event types (
vetting-approval, grant,
grandfather) dropped into the same log later — a repoint,
not a rebuild. Grandfather the 60 as 60 grant events;
never route the charter cohort through checkout.
- Rent the billing mechanics. Proration,
dunning, tax, invoicing are the processor's native subscription
primitives — own only the entitlement event + tier attribute.
6.
Decision — "Own the entitlement · rent the rails · defer the
machinery"
A hybrid that takes the winning move from each lens. Adopt
A's shape with C's estate contract; ship one paid tier (Guest→Member
self-serve) as the entire E8 v1.
6.1 Own
(the moat — cheap now, catastrophic to retrofit)
- The admission/billing-event log is the entitlement source of
truth. Append-only, immutable, member-keyed — the same-shaped
log as ADR-001, extended with a billing clock. Every admission, payment,
downgrade, refund, dispute, grant, and grandfather is an event:
event_id · member_sub (FK) · from_tier · to_tier · reason (payment | vetting-approval | grant | grandfather | refund | chargeback | lapse) · actor · effective_at · source_ref (processor event-id).
- Tier is a derived, read-time function of
(admission events) × (live subscription status as of now),
evaluated before any ranking or retrieval — never a
stored grant on an object, token, session, or vendor. This is I9
extended: entitlement now has a validity interval.
- A locally-owned, rebuildable subscription-status
projection is the read-path (fast, available, no third-party
call on the hot path). It is a derived view — rebuildable from the event
log + reconciliation — never an un-rebuildable authority.
- Enforcement reuses the estate contract: a
fail-closed, default-deny
evaluate(member, capability) → (allowed, reason, audit-event)
check at every surface/feature boundary (capability-gate's contract,
driven by live tier — not a startup-loaded YAML), with a structured
audit event per decision (the wallet's immutable JSONL shape). The
wallet's windowed-limit + kill-switch structure is the template for
usage quotas, not billing.
6.2
Rent (the commodity — don't hand-roll, don't let it become the
record)
- Checkout + PCI surface: hosted checkout / redirect
/ processor-tokenized fields only — SAQ-A, card data never on
our iron. The shell renders our chrome around
their fields; we never build a PAN input to match the design
system.
- Subscription mechanics: proration, dunning/retry,
tax/VAT, invoicing, receipts, plan changes — the processor's native
primitives. We own the tier transition they cause, not the
financial record.
- Webhooks as a signed event source: verify signature
→ dedup on
source_ref → append entitlement event
(idempotent, at-least-once) → recompute tier. Distinct per-env secrets
in the SOPS/age standard; the webhook signing secret is a
separate secret from the API key and rotates independently; the
API key is least-privilege (charge/subscription scope, not payout).
6.3 Defer (Torvalds'
cut — scoped, not killed)
- One paid tier for v1. Guest→Member self-serve
payment only. On
subscription.active write
tier: member; on canceled/past_due write
tier: guest — a single subscription→tier
function in one place, idempotent.
- Vetting/admission machinery is NOT built.
Member→Inner-Circle stays vetted/invite; the review-queue +
application-schema + invite-token system ships when the first
Member asks to upgrade, shaped around that real applicant
(admission-as-anti-abuse: the highest-value access is deliberately
not self-serve-purchasable). File the bead; do not guess the
form.
- Founding stays granted, never charged. Grandfather
the 60 as direct
grant events with
reason=grandfather — a one-time set, not a workflow, and
never routed through checkout.
- Keep the 4-value tier enum from day one (cheap — it
is just an ordering). Keep the P0 entitlement tier-leak eval, with T0/T1
fixtures until T2/T3 content exists. The append-only log +
tier-at-read + webhook-as-event + reconciliation loop are built from the
first charge — cheap up front, unrecoverable to retrofit.
7. Tradeoff matrix
| Tier stays sole permission authority (I10/I11) |
✅ |
❌ second system of record |
✅ |
❌ authority rented |
✅ by construction |
| Processor = billing truth only, never access |
✅ |
❌ |
✅ |
❌ |
✅ |
| No tier-leak on dispute/refund/lapse |
✅ (at-read) |
❌ drift on chargeback |
✅ |
❌ webhook-lag window |
✅ |
| PCI stays SAQ-A |
✅ (hosted) |
⚠️ vendor-dependent |
✅ (hosted) |
✅ (marketplace) |
✅ hosted checkout |
| GDPR / liability split clean |
✅ (we own tier) |
❌ vendor owns it |
✅ |
⚠️ marketplace is customer-of-record |
✅ |
| Time-to-first-dollar |
◑ integrate webhook |
✅ fast |
◑ |
✅ fast |
◑ (one webhook, one tier) |
| Ops-service count / small team |
✅ |
✅ (but forbidden) |
✅ |
❌ stateful billing service |
✅ (defer machinery) |
| Rent proration/dunning/tax |
⚠️ processor primitives |
✅ |
⚠️ processor primitives |
✅ |
✅ (processor-native) |
| Reversibility (swap = repoint) |
✅ one function |
❌ |
✅ |
❌ |
✅ |
| Estate pattern reuse |
◑ restated |
❌ |
✅ contract reused |
❌ |
✅ (contract, not code) |
8. Consequences
New locked invariants (promote into the foundation spec §7,
continuing I9/I10):
- I11 — Entitlement is a derived, read-time function of the
append-only admission/billing-event log and live subscription status,
reconciled against the processor. The processor is the source of truth
for billing, never for access. Tier is never a stored grant on an
object, a token, a session, or a vendor. (Kleppmann — the
single most important missing sentence, I9 extended with a billing
clock.)
- I12 — Payment webhooks are signature-verified,
replay-resistant (event-id dedup + timestamp window), and idempotent;
they are appended as events, never applied as state-mutating commands. A
dispute/chargeback/refund/failed-renewal is an attribute downgrade that
de-provisions every surface and the AI retrieval filter at the next
read. (Payments-security — webhook integrity is the whole
ballgame.)
- I13 — Card data never touches our iron. Checkout is
hosted/tokenized (SAQ-A); the shell renders our chrome around the
processor's fields, never our own PAN inputs.
(Payments-security — a first-class design-system constraint, not a
footnote.)
New required evals (the moat is only as trustworthy as
these):
- P0 — Entitlement tier-leak eval, fail-closed.
Golden set of (member fixture, subscription-state, event: downgrade /
refund / chargeback / lapse) → must-deny after
effective_at, within one read. The billing mirror of
ADR-001's P0 retrieval eval. Build before the first real
charge.
- P0 (required CI check) — Webhook signature + idempotency +
ordering test. Forged webhook rejected · replayed webhook
deduped on
source_ref · out-of-order downgrade/upgrade
lands correctly by per-member effective_at.
- P1 — Reconciliation-drift eval. Periodic pull
against the processor's authoritative subscription list detects and
repairs divergence from a dropped webhook. The leak window equals the
reconciliation interval — a stated durability parameter
(tight for money: minutes/hours, not days), not an accident.
E8's first task is the processor→entitlement seam, not the
admission machinery (Torvalds + Kleppmann): specify and build
the idempotent webhook handler keyed on source_ref + the
periodic reconciliation pull + the rebuildable subscription-status
projection + the single subscription→tier function, for
one paid tier (Guest→Member) — with the P0 entitlement
tier-leak eval built before the first charge. Vetting/admission
is a deferred bead ("design Inner-Circle admission when the first Member
asks to upgrade"); the 60 are grant events.
Estate seams (what we actually reuse):
- Capability-gate's fail-closed default-deny
evaluate → (allowed, reason, audit-event)
contract → the tier/feature entitlement check at every
surface boundary (contract, not the Ruby in-process gem).
- Agent-wallet's windowed-limit + kill-switch +
GuardrailCheckResult(allowed, reason, triggered) → template
for usage quotas, not billing.
- Signed audit-journal / immutable JSONL shape → the immutable
entitlement-transition audit trail (chargeback defense + dispute
forensics).
- SOPS/age secrets standard → processor API key + webhook signing
secret (separate secrets, per-env, least-privilege), never plaintext
.env, never the public repo, never chat.
- Payments processor itself → confirmed GAP; the
whole fiat side is greenfield.
9. Preserved dissent
(do not resolve away)
- One-tier-first vs. the vetted-community premise (Torvalds
vs. his own concession). Shipping Member-only risks anchoring
the brand as a "$X/mo course subscription" so the vetted "country club"
story never gets told — and a plausible reading is that the scarcity
and vetting are the product, making the 60 founders the exact
moment to stand up an Inner-Circle signal. The hybrid honors
this: the signal (a manual invite + a tier badge) is a column
write, not a review-queue build, so it needs zero admission
machinery. But the timing of the signal is a real
judgment call, not obviously deferrable — an acceptable, reversible
bet.
- "Don't build a billing system" (Kleppmann's self-dissent,
sharpened by payments-security). A small team owning
entitlements on raw charge-events will reinvent subscription billing
incompletely — the mirror of the chargeback leak is a silent revenue
leak (renewal fails, dunning never fires, member keeps access
free). The hybrid answers it by renting proration/dunning/tax
from the processor's native subscription primitives and owning
only the entitlement event. Trigger to revisit: if the
processor's native subscription tooling proves thin, D (subscription
middleware) becomes correct and A/C was a false economy — watch for it
at the four-tier expansion, not v1.
- Availability (answered within A, not against
it). Paid-content reads must not depend on the processor being
reachable. The read path hits the local rebuildable
projection (fast, available), reconciled against the processor
on a clock. That is neither "query the processor per read" nor "trust an
un-rebuildable cache" — the residual honest concession is that the leak
window equals the reconciliation interval, stated as a durability
parameter and tuned tight for money.
- "Borrow your way out via a vendor" (compliance dissent,
rejected). A bundled platform (B) or marketplace (D) shifts
refund/GDPR/chargeback burden to a vendor — but its
enrolment/subscription schema becomes a second permission
authority, violating Pillar 1 / I9 / I10, and it can't retrofit
tier-at-read or the admission flow. The compliance work is not "extra" —
it is the tax for owning your tier model, and owning it is what makes
the liability story defensible.
10.
Follow-up (beads to file in Phase B under the E8 epic)
- Lock invariants I11 + I12 + I13 into the foundation
spec §7.
- P0 entitlement tier-leak eval (fail-closed) —
before the first real charge.
- P0 webhook signature + idempotency + ordering test
as a required CI check; P1 reconciliation-drift eval
with a stated leak-window durability parameter.
- Specify the processor→entitlement seam: idempotent
webhook handler keyed on
source_ref + periodic
reconciliation pull + rebuildable subscription-status projection +
single subscription→tier function.
- Ship Guest→Member self-serve (one paid tier)
end-to-end; grandfather the 60 as
grant events;
never route Founding through checkout.
- Defer the Member→Inner-Circle admission machinery
to a bead ("design when the first Member asks to upgrade"); reuse the
fail-closed capability-gate entitlement contract at the surface
boundary.
- Processor secrets in SOPS/age (API key + signing secret separate,
per-env, least-privilege); processor DPA/SCC + refund/ToS/privacy policy
covering vetting-data retention, tier-downgrade consequences, and
GDPR/CCPA/PIPEDA rights.
- Annotate per-seam reversibility: entitlement authority /
tier attribute = irreversible; processor, checkout,
subscription-mechanics = repoint.
Panel transcript basis: estate-scout + payments-security /
compliance-&-legal / Kleppmann (data) / Torvalds (simplicity)
lenses, 2026-07-22. Follows the ADR-001 template; E8's entitlement model
is the direct extension of I9/I10.