Skip to main content

Waitlist

Struct Waitlist 

Source
pub struct Waitlist { /* private fields */ }
Expand description

A per-product waitlist.

Implementations§

Source§

impl Waitlist

Source

pub fn new() -> Self

No products allowed until Waitlist::products or Waitlist::any_product says otherwise; 7-day confirm TTL; referrals on.

Source

pub fn products( self, products: impl IntoIterator<Item = impl Into<String>>, ) -> Self

Exactly these product slugs may be joined.

Source

pub fn any_product(self) -> Self

Accept any product slug (single-product ventures that do not want an allowlist).

Source

pub fn confirm_ttl_days(self, days: u32) -> Self

How long a confirmation link stays valid (default 7 days).

Source

pub fn retention_days_pending(self, days: u32) -> Self

Purge pending entries untouched for this many days from the scheduled handler (default 30; issue #13 retention rule).

Source

pub fn status_redirect(self, url: impl Into<String>) -> Self

Where confirmation redirects to, as <url>?token=<status token> (default <public_url>/waitlist/status).

Source

pub fn referrals(self, enabled: bool) -> Self

Whether ref codes credit referrers (default true).

Source

pub fn answers_schema( self, validate: impl Fn(&Value) -> Result<(), String> + Send + Sync + 'static, ) -> Self

Validates the free-form answers JSON of a join (default: accept anything). Only runs when the join carries answers; pair with Self::require_answers to also reject joins without them.

Source

pub fn require_answers(self, required: bool) -> Self

Whether a join must carry answers (default false): with true, a body without the field — absent or null — is rejected as validation-failed.

Trait Implementations§

Source§

impl Default for Waitlist

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Module for Waitlist

Source§

fn personal_data(&self) -> &'static [PersonalDataSet]

The entry is anonymised, not deleted; the other two tables have no subject column at all (issue #265).

Why Anonymise and not Erase. A confirmed entry carries two numbers that belong to other people as much as to its owner: position is a dense per-product join order that is deliberately never recomputed, and referrals is a credit already granted. Delete the row and the queue acquires a hole nobody can explain and every referred_by pointing at its code orphans; keep it and null the person out of it, and the request is answered without rewriting anybody else’s place in the line. Migration 0005 exists for exactly this: email and email_normalized were NOT NULL, so the SET … = NULL this declaration promises could not have run.

referral_code is deliberately kept. It is a random 8-character string that names nobody once the address is gone, and it is what the referrals other people were credited for still resolve against.

Why id and not the address. GET /v1/privacy/export?subject=… puts the subject value in a URL, and URLs outlive their request in access logs, proxies and browser history — which is why this module’s own links carry the opaque row id and never the address (issue #135). Declaring email_normalized as the subject would have made a subject access request the one operation that writes an address into those logs.

Why the other two have no subject column. waitlist_position_lock is one row per product, genuinely names nobody, and is none. waitlist_send_cooldown is the harder case, declared unreachable, and its reason says so plainly: its primary key is the address and product the mail throttle counts against, so the address is inside a key rather than in a column of its own, and … WHERE <column> = ? cannot reach it. That is the same shape issue #266 describes for Outbox, and the table is core’s SendCooldown shape rather than this module’s, so giving it a column of its own is a change to every module that has one. Saying so is the honest answer available here; inventing a subject column that matched nothing would not be.

Source§

fn name(&self) -> &'static str

Kebab-case name; mounted at /v1/<name>.
Source§

fn version(&self) -> &'static str

env!("CARGO_PKG_VERSION"), surfaced by /__health.
Source§

fn requires(&self) -> &'static [Port]

Ports the module cannot run without; missing = build error.
Source§

fn optional(&self) -> &'static [Port]

Ports the module uses when present.
Source§

fn tables(&self) -> &'static [&'static str]

Table names this module owns; duplicates across modules are a build error.
Source§

fn emits(&self) -> &'static [&'static str]

Event names this module emits ("<module>.<event>"), listed by /__health.
Source§

fn public_writes(&self) -> bool

Whether the module has public write endpoints. Read more
Source§

fn migrations(&self) -> Migrations

The module’s migrations, embedded per dialect.
Source§

fn validate_config(&self, cfg: &dyn Config) -> Result<(), ConfigError>

Rejects invalid configuration: missing required keys or malformed values, reported together with the module name. Read more
Source§

fn router(&self, ctx: ModuleContext) -> Router

The module’s router, nested under /v1/<name>.
Source§

fn surface(&self) -> Surface

The module’s UI surface (ADR 0010): the actions a renderer may offer and the views that compose them. Input schemas come from the handler’s own body types (Action::input::<Body>()), so the declaration cannot drift from the route. Harness::build validates it; GET /__surface serves the composition. Default: nothing, and a module that declares nothing renders nothing.
Source§

fn scheduled<'a>( &'a self, ctx: &'a ModuleContext, cron: &'a str, ) -> BoxFuture<'a, Result<(), AnyError>>

Scheduled work (cron is the trigger expression). Default: none.
Source§

fn harness_api(&self) -> u32

Contract version, checked by Harness::build.
Source§

fn depends_on(&self) -> &'static [&'static str]

Modules this one’s schema sits on top of, by name. Read more
Source§

fn actor_kinds(&self) -> &'static [&'static str]

Actor kinds this module owns, each [a-z0-9_-]; duplicates across modules are a build error, and so is a kind with no handler registered through HarnessBuilder::actor. Read more
Source§

fn public_write_policy(&self) -> RoutePolicy

What actually protects those public writes, for a module with no ADR 0010 surface to hang a per-action RoutePolicy on (issue #143). Read more
Source§

fn signature_verification(&self) -> SignatureVerification

Which verifier proves this module’s RoutePolicy::Signature deliveries (issue #533): how a webhook handler checks a machine signature before acting. Read more
Source§

fn max_body_bytes(&self, _cfg: &dyn Config) -> usize

The largest request body any route of this module accepts, so a runtime can refuse an oversize body before buffering it (issue #440). Read more
Source§

fn streaming_routes(&self) -> &'static [StreamRoute]

Routes this module serves in streaming mode (issue #585), each with its own StreamRoute::max_bytes ceiling. Opt-in: the default is empty, and every existing route keeps the buffered behaviour it always had. Read more
Source§

fn max_blob_object_bytes(&self) -> u64

The largest object this module’s streamed and multipart blob writes may produce (issue #586), enforced by the ScopedBlob the harness hands the module on put_stream, upload_part and complete_multipart. Read more
Source§

fn self_check(&self) -> Vec<String>

Problems the module can find in itself, with no configuration and no environment: its own embedded data, checked against its own declarations. Read more
Source§

fn well_known(&self) -> Option<Router>

Routes this module serves at the root under /.well-known, for spec-mandated discovery documents (OIDC openid-configuration, jwks.json) that must live outside /v1 (issue #46). Paths are relative to the prefix: register /jwks.json, not /.well-known/jwks.json. Read more
Source§

fn events( &self, ) -> Vec<(String, Arc<dyn Fn(&Scope, Value) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn Error + Sync + Send>>> + Send>> + Sync + Send>)>

Handlers for events other modules emit; registered at Harness::build.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more