Configuration, environment, and session context.
System settings are stored encrypted in the database and accessed through a caching layer. Environment variables provide runtime configuration for the edge deployment.
The day counts a customisable listing offers, ascending: the priced counts
that fall within [1, duration_days] (duration_days is the maximum when
customisable_days is on). Empty for non-customisable listings.
Clamp a valid whole-day count to the supported booking range.
The per-ticket price (minor units) for booking days on a customisable
listing, or null when the listing isn't customisable or that count has no
configured price (and therefore isn't offered).
The site's own domain from its settings, falling back to the host the request named: a validated custom domain wins, then the Bunny subdomain. Every absolute-URL reader shares it, so the readers cannot disagree.
Get booking fee percentage from database. Returns 0 if not set.
Get the Botpoison public key from environment (safe to expose to browsers). Returns empty string when unset.
Get the Botpoison secret key from environment (server-side verification only). Returns empty string when unset.
Get the Bunny CDN API key from environment
Get the Bunny DNS subdomain suffix (e.g. ".tickets") from environment
Get the Bunny DNS zone ID from environment
Get the Bunny Edge Script ID from environment
Return the cached session if already resolved, or undefined if not yet resolved
Diagnostic key gating the verbose /health response. Empty when unset, in
which case /health only ever returns the plain liveness reply. Holding the
key reveals non-private build/runtime diagnostics (commit, build time) that
are useful for operators but needlessly helpful to an attacker.
DEFAULT_DB_HOST's provider: "turso" when set to it, "bunny" otherwise.
Get the Deno Deploy organization ID from environment.
Get the Deno Deploy organization slug used in managed production domains.
Get the Deno Deploy API token from environment.
Get the effective domain synchronously: the live request's own domain, or DEFAULT_DOMAIN otherwise. A live request that has not seeded its domain yet reads DEFAULT_DOMAIN, never the fallback — the fallback names the site for out-of-request reporting, and one request's host must not answer another's security-header decision.
Get allowed embed hosts from database (encrypted, parsed to array) Returns empty array if not configured (embedding allowed from anywhere)
Get an environment variable value Checks process.env first (Bunny Edge), falls back to Deno.env (local dev)
The shared secret authorizing the inter-instance site-credentials endpoint.
Get the READ_ONLY_FROM cutoff ISO string, or null if not set
Get the RENEWAL_URL, or null if not set
Get the Turso API token from environment.
Get the Turso database group from environment.
Get the Turso organization name from environment.
True when an attendee/booking row is a real ticket (quantity ≥ 1) rather than the no-quantity sentinel (quantity 0) — the one "is this a real ticket, not a ghost" test the readers, rosters, and exports share.
Check if this instance can build other sites.
Pure helper: is the current time within the warning window before cutoff?
Whether an listing can accept payments: a flat price, pay-what-you-want, or a customisable-days listing with at least one non-zero day-count price.
Whether an active provider has the credentials a sale needs.
Check if the system is read-only: an update is running, or the READ_ONLY_FROM cutoff is in the past
Pure helper: is the site read-only based on a cutoff timestamp?
Check if the site shows a pre-expiry warning banner
Type guard: a non-null, non-array object (a Record shape).
Whether we are serving a real, resolved host rather than the default. Gates
HTTPS-only behaviour — Secure/__Host- cookies and the HSTS header — which
must stay off for local development on DEFAULT_DOMAIN.
Check if an update is in progress: UPDATE_IN_PROGRESS is set non-empty. The deploy workflows set it around a site code update, and every write refuses while it is set.
Load the effective domain from DB, falling back to the request URL hostname.
Coerce an arbitrary stored/parsed value into a clean DayPrices map. Keeps only whole-number day counts in [1, MAX_DURATION_DAYS] mapped to finite, non-negative whole-number minor-unit prices. Everything else is dropped. Used on both the DB read path and form parsing so the rest of the code can treat the map as already-valid.
Parse a flash cookie value into type, message, optional result, and the optional form re-fill stash token.
Parse a string into a positive integer for warning days. Returns defaultVal on bad input.
Pick the value for the active payment provider from a per-provider set, or
null when no provider is active. The set is keyed by the provider union,
so a caller cannot forget a provider: leaving one out is a compile error at
the call site rather than a silent null on a live site.
Get a required environment variable, throwing if not set.
Use this instead of getEnv(key) as string when the variable must exist.
Reset effective domain cache back to the default (for testing).
Seed the effective domain from the request's own hostname.
Store the resolved session in the current request's store
Set effective domain directly (for testing): the live request's store when one is being served, the out-of-request fallback otherwise.
The fields shared by every named, slugged content record whose free-text
columns are stored encrypted: the body/meta blobs, the display name, and the
/slug permalink paired with its plaintext HMAC blind index (slug_index).
Both site pages and news posts build on this.
A membership of listing_id in group_id, hydrated for the package editor
and booking flow. A listing may belong to several groups. package_price
(minor units) is the per-listing override when the group is a package, read
from the group dimension of listing_prices (not a column on the join
table): null means no override (use the listing's own price), 0 means
explicitly free in the package, and a positive value overrides the price.
quantity (≥1, stored on the join row) is how many of this listing one unit of
the package includes. Both are ignored for non-package groups.
A group paired with the active member listings used to decide and render it.
- active
- assign_built_site
- attachment_name
- attachment_url
- bookable_alone
- bookable_days
- can_pay_more
- closes_at
- created
- customisable_days
- date
- day_prices
- description
- duration_days
- fields
- hidden
- id
- initial_site_months
- listing_type
- location
- max_attendees
- max_price
- max_quantity
- maximum_days_after
- min_quantity
- minimum_days_before
- months_per_unit
- name
- non_transferable
- purchase_only
- slug
- slug_index
- thank_you_url
- unit_price
- use_defaults
- uses_logistics
- webhook_url
An owner-defined price modifier (surcharge / discount / add-on). calc_value
is the positive magnitude the owner entered (a fixed amount in major currency
units, a percentage, or a multiplier); direction chooses charge vs discount.
A news post shown on the public /news page. All free-text columns are
stored encrypted; created stays plaintext (like listings) so the
newest-first ordering and the RSS pubDate never need a scan-and-decrypt.
slug is the /news/:slug permalink (auto-generated from the created date
and the name at creation, then immutable); slug_index is its blind index.
One ordered membership edge: item (of item_type) sits inside page_id
at sort_order. Keyed on the composite (page_id, item_type, item_id).
A link between an agent user and a logistics agent (van/crew) they drive. Many-to-many: a user may cover several agents and an agent several users.
Admin role levels
Admin API listing shape — all listing fields except internal indices. Used by both admin JSON API and admin templates to ensure consistent field exposure. Snake_case keys match the DB schema.
A single row in the attendee table: an attendee plus the listings the row covers, in display order. Roster/check-in tables render one row per booking line; the browsing tables group an attendee's lines into one row.
Individual contact field name
Required name+email with optional phone/address/special_instructions from ContactInfo
Attendee contact details — the core PII fields collected at registration
The subset of listing fields needed to reason about day-count pricing.
Per-day-count ticket prices for "customisable days" listings, in minor
units, keyed by the number of days booked. e.g. { 1: 1000, 2: 1800 }
means a 1-day booking costs 1000 and a 2-day booking 1800. Only counts
present here are offered to the visitor.
The attendee fields the shared attendee table and its column registry
actually read. A full decrypted Attendee satisfies it, and so does
a field-selected read that skipped the money subqueries — so the browsing
tables render rows that never computed the money columns
(src/shared/db/attendees/select.ts).
One part of an email template: the subject, the html body, or the text.
The persisted email template types: the attendee confirmation and the admin notification. The discriminator the renderer, the settings store, and the admin forms all key on. A plain union, because nothing validates a string against it — every value comes from a typed call.
Visual level of a flash message: a positive result, a failure, or a neutral acknowledgement.
A listing's contact fields: comma-separated ContactField names, or empty
for name only. parseListingFields enforces it at runtime.
Listing type: standard (one-time) or daily (date-based booking)
Ids for the settings nags that prompt required or recommended setup.
The public /news list projection: a summary plus the post's first image (the shared ItemImageColumns columns).
The narrow list projection — id, created, slug, name, snippet — for readers
that render no images (the RSS feed, the admin list). Never the large
content/meta_* blobs (cold-start efficiency, like SitePageNavRow).
Supported payment provider identifiers
The kind of thing a SitePageItem points at. Exhaustive union — a new
member is a compile error at every Record<SitePageItemType, …> dispatch.
The listing values needed to place a listing in the shared sort order: which tier it belongs to (its type and date), its name for the within-tier sort, and — for a daily listing — the values that decide its next bookable date. A read that only lists or picks listings can select these columns alone and skip the whole listing record.
UI theme
Schema for admin role levels.
Every admin role level — for actions every authenticated user must reach (e.g. logout). Derived from AdminLevelSchema so a new role propagates automatically instead of being hand-listed here.
All valid contact field names (runtime array matching the ContactField union)
Schema for an individual contact field name
Admin role levels that may create/edit listings & groups: the back-office
staff plus the content-only editor. Gates the listing/group content
routes editors opt into; deliberately excludes agent.
Strict stored shape for per-day-count prices.
Admin role levels that may reach the delivery run sheet
(/admin/deliveries): staff plus delivery agents — the run sheet's
long-standing audience, with the content-only editor excluded.
Deno Deploy app names: 3–32 chars, lowercase letters, numbers, hyphens.
Admin role levels that may work a door: the staff plus the door-only
scanner, gating the scanner pages, the scan API, and the check-in toggle.
Schema for the kind of item an image can be attached to.
Type guard: check if a string is a valid AdminLevel
Check if Botpoison spam protection is configured. Requires both BOTPOISON_PUBLIC_KEY and BOTPOISON_SECRET_KEY to be set. This gates the public contact form feature.
Check if Bunny CDN pull zone management is enabled Requires both BUNNY_API_KEY and BUNNY_SCRIPT_ID to be set
Check if the Bunny hosted database provider is enabled (requires BUNNY_API_KEY).
Check if Bunny DNS subdomain feature is enabled. Requires BUNNY_API_KEY and BUNNY_DNS_ZONE_ID to be set.
Type guard: check if an arbitrary string is a valid ContactField
True for roles that may create/edit listings & groups (owner/manager/editor).
True for roles that may reach the delivery run sheet (owner/manager/agent).
Check if Deno Deploy hosting has its token, organization ID, and domain slug.
True for roles that may work a door (owner/manager/scanner).
Whether the inter-instance site-credentials endpoint is enabled. Off unless MAIN_INSTANCE_KEY is set, so a non-builder instance never exposes it. The key is a high-entropy shared secret the operator passes to the upgrade workflow at trigger time (it is never stored in GitHub).
Type guard: check if an arbitrary string is a valid ListingType
True only for the owner role.
Type guard: check if a string is a valid PaymentProviderType
Type guard: check if a string is a valid PaymentProviderSetting
Type guard: is this string a valid SitePageItemType?
True for roles that may edit public-site content (owner/editor).
True for back-office staff (owner/manager).
Whether Turso hosting has its API token, organization, and group set.
Schema for a listing type: standard (one-time) or daily (date-based booking)
Upper bound on multi-day booking duration. Each day in a booking range adds a per-day clause to the atomic capacity SQL, so the cap keeps that statement bounded regardless of which write path set the value.
Schema for supported payment provider identifiers
Persisted payment-provider setting: an explicit provider, "none" (admin saved payments-disabled), or absent (never saved — drives the settings nag).
Admin role levels that may edit the public-facing site content (homepage,
contact, order intro). Site editing has always been owner-only; the editor
role joins it, but manager stays excluded — owner+editor, NOT the broader
CONTENT_ADMIN_LEVELS.
Schema for the kind of thing a SitePageItem points at.
Admin role levels that are back-office staff (not delivery agents).
Turso database names: non-empty, at most 63 chars.
Usage
import * as mod from "docs/config.ts";