Expand description
monetize-embed — what a product compiles in. One struct, one question: does this tenant get to do this, right now? Answered from verified facts held in memory, in nanoseconds, with monetize down or not.
monetize-server ──signed Snapshot file──▶ EntitlementCache::refresh (start, catch-up)
monetize-server ──signed EntitlementFact─▶ EntitlementCache::push (every change)
product choke point ──────────────────▶ EntitlementCache::allows (every request)§The ladder table, as the product sees it
| state | Write (push, LFS upload) | Read (owner clone/fetch) | AnonymousRead | Admin (UI) |
|---|---|---|---|---|
| Free, Paid | allow | allow | allow (product still applies Public) | allow |
| Grace | allow, EntitlementCache::notice has the warning line | allow | allow | allow |
| Suspended | refuse, named reason + URL | allow | refuse | refuse: pay page only |
| Retention | refuse | allow (export) | refuse | refuse: pay page + export |
Two rules, not negotiable: never hold data hostage — Read is allowed in every
state, a lapsed tenant can always clone their own repositories; and refuse before
the bytes — the product asks at authorise_service, before a pack is read, and
puts Verdict::Refuse::reason_line in report-status.
§What is, and is not, decided here
- The
statein a fact is monetize’s verdict at push time. The cache does not re-derive it as the clock moves — it does not know the policy’s day counts, and the product contract says a product keeps serving on its cached entitlement when monetize is unreachable. Ladder transitions arrive as new pushes. - An unknown tenant is
Free: the product’s default, never a refusal. - Visibility (
Public) and quotas (caps) are the product’s own checks; this crate only says whether the entitlement allows the kind of action.
§Cost of the read path
EntitlementCache::allows is one ArcSwap::load (an atomic increment on a
debt-slot, ~2 ns, no lock, no allocation), one BTreeMap lookup on the tenant name,
and a match. A refusal allocates its two strings; an allow allocates nothing. Writes
(push, refresh) clone the map and swap the pointer — O(tenants), taken by the
rare path on purpose so the hot path never contends.
Re-exports§
pub use signing::SignatureError;pub use signing::Snapshot;pub use ticket::verify_ticket;pub use ticket::ActorTicket;pub use ticket::SeenNonces;pub use ticket::PURPOSE_ORDER;pub use ticket::PURPOSE_READ;pub use ticket::PURPOSE_RENEW;
Modules§
- civil
- Proleptic Gregorian civil dates from unix milliseconds, pure integer arithmetic
(Howard Hinnant’s
days_from_civil/civil_from_days). Here rather than in core because both sides need it: core to compute a period’s end and a reference’s date label, embed to printplan expired 2026-10-01in a refusal line. No chrono: the product should not inherit a date library for one string. - signing
- The one canonical form an entitlement is signed over, and the verify side.
- ticket
- The actor ticket: the appliance’s word that this human may renew this tenant — minted by the product’s appliance, verified here.
Structs§
- Entitlement
Cache - Entitlement
Fact - The verdict monetize pushes back. Product-agnostic; the plugin maps it to the
product’s own enum (gunnar:
EntitlementState). - KeyError
- Tenant
Id - A tenant, as the product names it. gunnar: the
Namespacename (team/sub). - Verifying
Key - Re-exported so a caller that must CHECK a signature — a product, or the operator console — links one crate and cannot end up on a different ed25519-dalek than the one the canonical form was verified against. An ed25519 public key.
Enums§
- Action
- What the product is about to do on behalf of (or to) a tenant.
- Refresh
Error - State
- The ladder. Numbers (grace/retention days) are the deployment’s policy
(
monetize::Policy), not the enum’s. - Verdict