Specify the entitlement and subscription model #20

Closed
opened 2026-08-02 03:05:54 +00:00 by christian · 2 comments
Owner

Question

Under tools-not-access, every entitlement check has to fall on a tool rather than on visibility or submission. That constraint is unusual and needs to be made concrete before it can be built.

Resolve: what is in the free vendor tier versus the paid workspace; where entitlement is enforced; how a vendor invited by a retailer experiences the free path end to end without ever hitting a wall; and what the upgrade moment looks like. Price level itself stays in the fog until this is settled.


Parent: #1

## Question Under tools-not-access, every entitlement check has to fall on a tool rather than on visibility or submission. That constraint is unusual and needs to be made concrete before it can be built. Resolve: what is in the free vendor tier versus the paid workspace; where entitlement is enforced; how a vendor invited by a retailer experiences the free path end to end without ever hitting a wall; and what the upgrade moment looks like. Price level itself stays in the fog until this is settled. --- Parent: #1
christian added the
wayfinder:grilling
wayfinder:ticket
labels 2026-08-02 03:05:54 +00:00
christian added a new dependency 2026-08-02 03:06:26 +00:00
christian self-assigned this 2026-08-02 04:22:16 +00:00
Author
Owner

Resolution

Four decisions. The through-line: the paid feature is persistence, not access — and every
consequence below falls out of holding that line against the cases that try to bend it.

1. Free vendors do everything; nothing persists

A free vendor bids on unlimited RFPs, attaches any document, answers any question, submits, and
tracks. Nothing is withheld and nothing is capped. What they don't get is durability: no vault,
no reused answers, no cross-retailer profile, no accumulated acceptance graph. Every RFP starts
from zero.

Paying makes the work durable.

Why this line and not the others. The upgrade moment writes itself — it is the second retailer
asking for the same certificate of insurance. That is precisely the cross-retailer white space the
ecosystem research identified as the strongest unmet need, and this monetises it without ever
becoming a toll. The free path stays genuinely complete, so no vendor is ever blocked from
responding to their own customer.

Volume caps were rejected outright. A cap is access-gating wearing different clothes. A vendor
who hits one mid-sourcing-event is blocked from bidding, which is the exact Ariba/ISNetworld
resentment mechanic the whole model exists to avoid.

Storage-free/intelligence-paid was rejected because it puts your costs on the free side —
storage, validation, expiry recomputation on a clock — which is the shape that forces a free
promise to move later.

What it costs. A vendor who only ever sells to one retailer never has a reason to pay, and on
any given roster that may be most of them. Revenue concentrates in multi-retailer vendors, and the
addressable population is smaller than the roster count suggests.

2. On lapse, documents stay; the machinery stops

The vault becomes a read-only archive. The vendor views and downloads everything, forever. What
stops is the reuse machinery: no auto-attach to new RFPs, no expiry tracking, no acceptance-count
accrual, no cross-retailer profile. They bid exactly like a free vendor — uploading each time.

Nothing is ever held hostage. Custody of a vendor's own documents is never the lever; the
capability is. Re-upgrading is trivial because the vault is still there and simply wakes up.

Already settled and unaffected: documents released to a retailer on a past RFP stay released.
Delivery is not retractable. This decision governs only the vendor's own reusable copy.

What it costs. Storage is carried for lapsed accounts indefinitely. Acceptable while the
catalogue is certificates and PDFs; it becomes a real cost question if the catalogue ever grows to
large media, and that is a constraint on the catalogue ticket.

3. Entitlement is resolved once into actor context, enforced per feature

Entitlement is not row visibility, and must never be expressed as it. RLS answers whose row is
this
; a lapsed vendor still owns every row they owned yesterday. Folding entitlement into policies
would make lapse into row-disappearance — the freeze model rejected above, arriving through the
back door — and would leave a billing bug capable of causing data loss and a policy bug capable of
causing revenue.

So: the request middleware that sets the actor GUCs also resolves the org's entitlement and
carries it in the same context object
. Each feature that costs money asserts against it at its
own entry.

Why per-feature rather than one chokepoint. Entitlement is not an operation-level property.
Writing a document is free; writing it for reuse is paid — the same INSERT serves both, so an
operation deny-list would have to encode intent it cannot see. The enforcement points are also
genuinely heterogeneous: most are request-scoped, but expiry recompute is a scheduled job with no
request at all
and needs the same answer from the same place.

Useful side effect: the assertion sites are the paid-feature list. The pricing page is
derived from the code rather than maintained alongside it.

A leak this exposed, and the principle that closes it. The vault decision established that
document expiry removes a vendor from future audience evaluations. If the expiry scheduler were
entitlement-gated, a lapsed vendor would stay qualified forever on stale certificates — a retailer
awarding against an expired policy because a third party stopped paying us. Unacceptable.

The rule: entitlement gates vendor-facing convenience, never retailer-facing correctness.
Expiry state is computed for every vendor regardless of billing; what a lapsed vendor loses is the
reminders, the dashboard, and the auto-attach — not the retailer's ability to see that their
certificate lapsed. This principle governs every future entitlement assertion and belongs in the
layout as a comment on the assertion helper.

4. No sponsorship in v1, but the seam stays

The vendor pays or doesn't; the retailer never appears in the billing relationship. The entitlement
record still carries a payer reference that in v1 always equals the beneficiary org — one
column now, no migration later.

Structurally impossible regardless, and worth stating: a retailer can never require a paid
subscription in order to bid. Free vendors bid fully, so the ISNetworld mechanic — buyer mandates,
supplier pays — cannot be built here even if a retailer asks for it.

Why defer the voluntary version. Sponsorship adds a payer/beneficiary split, sponsor-revocation
semantics (does a vendor drop to read-only mid-RFP?), a consent question (does a vendor want their
buyer knowing they subscribe?), and it makes a sponsored vendor's acceptance graph partly bought by
one of the retailers in it. All for a revenue path with no evidence behind it. The two-sided story
is currently unusually clean — retailers never pay, vendors buy tools — and sponsorship is the
first thing that blurs it.

What it costs. You give up the fastest network-seeding lever: a retailer who would put its top
fifty suppliers on the rail tomorrow.

Constraints handed to other tickets

  • Schema: entitlement record with a payer reference; vault documents durable through lapse
    (lapse changes capability, never row ownership); derived reuse state gated, derived expiry state
    not.
  • Data release: RLS policies must not reference entitlement at all. Two mechanisms, two failure
    modes, no overlap.
  • Catalogue: indefinite retention for lapsed accounts is a real cost input to which types ship.
  • App framework: the free path must be demonstrably complete end to end, and the upgrade moment
    is the second retailer asking for a document the vendor has already supplied once. That moment is
    a screen, not a pricing page.
  • The layout needs a scheduled-job entry point that establishes actor context without a request,
    and an entitlement assertion helper carrying the correctness principle above.
## Resolution Four decisions. The through-line: **the paid feature is persistence, not access** — and every consequence below falls out of holding that line against the cases that try to bend it. ### 1. Free vendors do everything; nothing persists A free vendor bids on unlimited RFPs, attaches any document, answers any question, submits, and tracks. Nothing is withheld and nothing is capped. What they don't get is **durability**: no vault, no reused answers, no cross-retailer profile, no accumulated acceptance graph. Every RFP starts from zero. Paying makes the work durable. **Why this line and not the others.** The upgrade moment writes itself — it is the second retailer asking for the same certificate of insurance. That is precisely the cross-retailer white space the ecosystem research identified as the strongest unmet need, and this monetises it without ever becoming a toll. The free path stays genuinely complete, so no vendor is ever blocked from responding to their own customer. **Volume caps were rejected outright.** A cap is access-gating wearing different clothes. A vendor who hits one mid-sourcing-event is blocked from bidding, which is the exact Ariba/ISNetworld resentment mechanic the whole model exists to avoid. **Storage-free/intelligence-paid was rejected** because it puts your costs on the free side — storage, validation, expiry recomputation on a clock — which is the shape that forces a free promise to move later. **What it costs.** A vendor who only ever sells to one retailer never has a reason to pay, and on any given roster that may be most of them. Revenue concentrates in multi-retailer vendors, and the addressable population is smaller than the roster count suggests. ### 2. On lapse, documents stay; the machinery stops The vault becomes a **read-only archive**. The vendor views and downloads everything, forever. What stops is the reuse machinery: no auto-attach to new RFPs, no expiry tracking, no acceptance-count accrual, no cross-retailer profile. They bid exactly like a free vendor — uploading each time. **Nothing is ever held hostage.** Custody of a vendor's own documents is never the lever; the capability is. Re-upgrading is trivial because the vault is still there and simply wakes up. **Already settled and unaffected:** documents released to a retailer on a past RFP stay released. Delivery is not retractable. This decision governs only the vendor's own reusable copy. **What it costs.** Storage is carried for lapsed accounts indefinitely. Acceptable while the catalogue is certificates and PDFs; it becomes a real cost question if the catalogue ever grows to large media, and that is a constraint on the catalogue ticket. ### 3. Entitlement is resolved once into actor context, enforced per feature **Entitlement is not row visibility, and must never be expressed as it.** RLS answers *whose row is this*; a lapsed vendor still owns every row they owned yesterday. Folding entitlement into policies would make lapse into row-disappearance — the freeze model rejected above, arriving through the back door — and would leave a billing bug capable of causing data loss and a policy bug capable of causing revenue. So: the request middleware that sets the actor GUCs **also resolves the org's entitlement and carries it in the same context object**. Each feature that costs money asserts against it at its own entry. **Why per-feature rather than one chokepoint.** Entitlement is not an operation-level property. Writing a document is free; writing it *for reuse* is paid — the same INSERT serves both, so an operation deny-list would have to encode intent it cannot see. The enforcement points are also genuinely heterogeneous: most are request-scoped, but expiry recompute is a **scheduled job with no request at all** and needs the same answer from the same place. **Useful side effect:** the assertion sites *are* the paid-feature list. The pricing page is derived from the code rather than maintained alongside it. **A leak this exposed, and the principle that closes it.** The vault decision established that document expiry removes a vendor from future audience evaluations. If the expiry scheduler were entitlement-gated, a lapsed vendor would stay qualified forever on stale certificates — a retailer awarding against an expired policy because a *third party stopped paying us*. Unacceptable. The rule: **entitlement gates vendor-facing convenience, never retailer-facing correctness.** Expiry state is computed for every vendor regardless of billing; what a lapsed vendor loses is the *reminders, the dashboard, and the auto-attach* — not the retailer's ability to see that their certificate lapsed. This principle governs every future entitlement assertion and belongs in the layout as a comment on the assertion helper. ### 4. No sponsorship in v1, but the seam stays The vendor pays or doesn't; the retailer never appears in the billing relationship. The entitlement record still carries a **payer reference that in v1 always equals the beneficiary org** — one column now, no migration later. **Structurally impossible regardless, and worth stating:** a retailer can never require a paid subscription in order to bid. Free vendors bid fully, so the ISNetworld mechanic — buyer mandates, supplier pays — cannot be built here even if a retailer asks for it. **Why defer the voluntary version.** Sponsorship adds a payer/beneficiary split, sponsor-revocation semantics (does a vendor drop to read-only mid-RFP?), a consent question (does a vendor want their buyer knowing they subscribe?), and it makes a sponsored vendor's acceptance graph partly bought by one of the retailers in it. All for a revenue path with no evidence behind it. The two-sided story is currently unusually clean — retailers never pay, vendors buy tools — and sponsorship is the first thing that blurs it. **What it costs.** You give up the fastest network-seeding lever: a retailer who would put its top fifty suppliers on the rail tomorrow. ### Constraints handed to other tickets - **Schema**: entitlement record with a payer reference; vault documents durable through lapse (lapse changes capability, never row ownership); derived reuse state gated, derived expiry state not. - **Data release**: RLS policies must not reference entitlement at all. Two mechanisms, two failure modes, no overlap. - **Catalogue**: indefinite retention for lapsed accounts is a real cost input to which types ship. - **App framework**: the free path must be demonstrably complete end to end, and the upgrade moment is the second retailer asking for a document the vendor has already supplied once. That moment is a screen, not a pricing page. - **The layout** needs a scheduled-job entry point that establishes actor context without a request, and an entitlement assertion helper carrying the correctness principle above.
Author
Owner

Partially superseded by author direction

Decision 1 is void. There is no free tier; the floor is $100/month. Persistence is no longer the
paid feature — it moved into the base tier, and the paid axis moved up to seats and AI capability.
See Specify the vendor pricing and tier structure.

Decision 2 stands, and matters more than before: the vault stays readable on lapse.

Decision 3 stands unchanged — entitlement resolved once into actor context, asserted per feature,
never expressed as row visibility. It gains an access-level assertion at the participation boundary.

Decision 4 is downgraded from declined to deferred. Retailer sponsorship was ruled out of
scope as an unevidenced growth lever. Under a hard access gate it becomes the only proposed answer
to the supplier-diversity problem — a retailer-side adoption risk on the side that is supposed to be
free. Pinned with onboarding by author direction.

The governing principle is restated, since entitlement now gates access rather than convenience:

Entitlement gates what a vendor may newly undertake, never what a retailer has already come
to rely on
.

Same intent, corrected scope. In-flight participation survives lapse; eligibility never depends on
billing; the expiry scheduler still runs for every vendor regardless of who is paying.

## Partially superseded by author direction **Decision 1 is void.** There is no free tier; the floor is $100/month. Persistence is no longer the paid feature — it moved into the base tier, and the paid axis moved up to seats and AI capability. See [Specify the vendor pricing and tier structure](https://gitea.stephenmann.io/christian/helmdocs-proposal-system/issues/34). **Decision 2 stands**, and matters more than before: the vault stays readable on lapse. **Decision 3 stands unchanged** — entitlement resolved once into actor context, asserted per feature, never expressed as row visibility. It gains an access-level assertion at the participation boundary. **Decision 4 is downgraded from *declined* to *deferred*.** Retailer sponsorship was ruled out of scope as an unevidenced growth lever. Under a hard access gate it becomes the only proposed answer to the supplier-diversity problem — a retailer-side adoption risk on the side that is supposed to be free. Pinned with onboarding by author direction. **The governing principle is restated**, since entitlement now gates access rather than convenience: > Entitlement gates **what a vendor may newly undertake**, never **what a retailer has already come > to rely on**. Same intent, corrected scope. In-flight participation survives lapse; eligibility never depends on billing; the expiry scheduler still runs for every vendor regardless of who is paying.
christian referenced this issue from a commit 2026-08-03 21:21:06 +00:00
Sign in to join this conversation.
No description provided.