Skip to content
Code Recycle

Component · for humans & their agents

Membership Gating

verified · first-partyactively maintained$0 during beta (was $79)

A null expiry means perpetual, and reading it as "no date, therefore lapsed" revokes every lifetime licence you ever sold — silently, with the page simply showing less.

by saltyhash · Code Recycle admin

Get it free — beta

Every claim on this page is refundable if it is untrue — refund policy.

Building it yourself: ~2h of agent time across about 4 attempts. Your credits are already paid for, so that feels free — but they are rivalrous: those are hours not spent on the part only you can build. And this one fails quietly when it is wrong, so the attempt that looks finished may not be. $79.

24 tests. Pure functions, zero dependencies, ESM. No database — you pass the rows.

The bug this exists to prevent

Access checks look like boolean arithmetic and are full of states that read as the opposite of what they mean.

  • expiresAt: null is perpetual, not missing. A one-off source purchase never expires. Treat
  • null as absent and every perpetual licence in your catalogue lapses at once, with no error
  • anywhere — the page just renders less.
  • state: "active" with a period end in the past is NOT live. A cancellation that has not
  • been processed yet leaves exactly that row. Trust the state alone and a lapsed member keeps
  • reading paid content indefinitely.
  • Demo rows must never grant. Seeded demo entitlements and subscriptions exist to make a
  • storefront look populated. If one unlocks paid detail, the hole is invisible: every visible
  • part of the page is correct.
  • Expiry exactly at now is lapsed, not live. Asserted directly, because the boundary is
  • where this is decided and >= versus > is a coin flip nobody revisits.

The two failure directions are deliberately opposite

Access fails CLOSED. If you cannot determine whether someone paid, the answer is no. Answering "yes" on an error is how withheld content leaks to everyone during an outage — at exactly the moment nobody is reading logs.

Disclosure fails OPEN. A listing with no member-section marker is entirely public. A missing marker means the content was never meant to be gated, so showing it is correct; hiding everything unmarked would blank most of a catalogue on a typo.

Those two rules contradict each other on purpose, and getting either backwards is a bad day.

Ownership is reported before membership

When both apply, the reason returned is owns_product, not active_membership — ownership is the more durable claim, because a membership lapses and a purchase does not. That matters for what you tell the reader: "included with your membership" is wrong and alarming for someone who bought the thing outright.

Splitting a listing

discloseListing cuts a description at a heading marker into public and member halves, and withheldNotice produces the line telling a non-member that detail exists behind it. The marker must be a heading on its own line — matching it mid-paragraph would split prose at a word.

What this does NOT do

No database, no session handling, no authentication, no payment provider. You fetch the entitlement and subscription rows and pass them in; this decides. That is why it is testable without a database, and the fetch layer is the easy half.

No markdown parsing. The split is structural, on a heading line.

Provenance, stated plainly

This was written for Code Recycle's own storefront in August 2026 and is in production there, which is a matter of days rather than years. The rules encoded above each come from a specific reasoned failure, and the suite pins every one — but it does not have the mileage of the older listings in this catalogue, and you should read it rather than assume it.

Verified

24 tests: no rows refusing by default, active/unrevoked/unexpired entitlements granting, null expiry as perpetual, expired/revoked/inactive refusing, expiry exactly at now lapsing, active and trialing granting, past_due/paused/canceled/expired refusing, an active state with a lapsed period refusing, a missing period end granting, demo rows never granting, a real row alongside a demo one still granting, and ownership reported ahead of membership.

Not covered: no test of the database layer, because there isn't one here. Nothing verifies your rows are shaped as this expects — that mapping is yours and is where a real integration would break.

01Capabilities

Does

  • + Role-based access control
  • + Commerce operations
  • + Data exposure boundary

Doesn’t

  • No exclusions declared

02Requirements & stack

Depends on

No declared dependencies

Credentials needed

None declared

Stack

03Community

No endorsements yet

No verified confirmations yet — be the first.

Confirmations come from verified purchasers, installers, vetted reviewers, or an installation outcome your org reported through the agent tools. They grade quality — security is verified separately, and community votes can never override the security gate.

Open an issue

Sign in to confirm — weight comes from verified usage, not vote count.

Nobody has reported anything yet — a success counts as a report too.

04Trust Passport

Full passport →
–/100

0/0 automated components pass. An automated score is never a security guarantee.

✓ Verified · first-partyreviewed Sep 20, 2026 · re-verification due Dec 19, 2026
  • publisher identity Publisher status verified; 1 verification(s) on file
  • malicious pattern scan No known malicious-behavior patterns across 10 source file(s) plus listing text
  • capability contract All 0 observed capability reference(s) match the declared manifest
  • agent safety scan No injection patterns in agent-readable content
  • provenance No release signature or provenance attestation
  • behavioral sandbox Not performed in this environment — requires the production isolated runner (docs/sandbox-requirements.md). No untrusted code is ever executed on the application host.

Every listing must pass this review before it can be sold, and it is re-run on every release. Verification describes what we checked — it is not a guarantee that the software is safe.

VersionChannelReleasedNotes
0.1.0stableAug 11, 2026Initial extraction.