Tenant compliance defaults — the opt-in envelope
Most Orbit defaults you can tune sit behind per-channel or per-campaign settings pages. Two of them do not: the GDPR erasure cooling-off window, and the default quiet-hours window campaign sends use when a campaign does not set its own. Because both apply org-wide, Orbit gates them behind an opt-in envelope: a master org toggle and one sub-toggle per feature, both of which must be the boolean valuetrue before the
resolver reads your values. Until that happens, the platform constants
apply — 7 days for erasure cooling-off, 21:00–09:00 for quiet hours —
for every tenant, so flipping anything has no surprise effect.
1. Why these two need an envelope
The erasure cooling-off and quiet-hours defaults started as platform constants because almost nobody changed them. That was right until it was wrong:- A strict GDPR reading (for example some EU supervisory authorities) wants erasure processed with no cooling-off delay; an enterprise review workflow may legitimately want 14 days to inspect a request before deleting. Both had to leave the platform constant and explain themselves later.
- 21:00–09:00 lines up with US TCPA expectations; EU/UK B2B email traffic often needs evenings, and the platform default made that awkward for no reason that survived scrutiny.
2. The opt-in envelope shape
The envelope lives on your organizationsettings object. Two levels
must both be true before the resolver honors your values:
value === true.
String "true", number 1, and every other truthy shape are rejected.
This is deliberate: a misconfigured admin UI or a patch script writing
string values must not flip org-wide behavior silently. If either the
master toggle or the feature sub-toggle is missing, false, or not the
boolean true, the envelope is closed and the resolver falls through to
the platform constants.
The override values themselves live on typed organization columns,
not in settings JSON, so PostgreSQL CHECK constraints bound what can
persist:
The two quiet-hours columns exist because
start may exceed end
(21:00–09:00 overnight), which a single “window” field cannot express.
Erasure values above 90 days are rejected at the column and clamped
again in the resolver as defense-in-depth.
3. What the resolver returns
Two resolution paths read the org row once, then decide:- Erasure cooling-off. If the envelope passes for
erasure, the resolver readsgdpr_erasure_cooling_off_days, floors it to a whole number, and clamps it into0–90. Anything non-numeric, missing, or out of range resolves to the platform constant (7), never to a junk value the DELETE scheduler must serialize later. - Quiet hours. If the envelope passes for
quiet_hours, the resolver reads both hour columns, floors each, clamps into0–23, and returns{ start, end }. Campaign code hands these to its quiet-window helper; a degenerate window (start equals end) or an overnight window (start above end) is still a legitimate configuration and is returned as-is.
settings in
one SELECT, keeps the read inside a small cache, and never throws —
every failure degrades to the platform constants. That is the fail-safe
posture you want for an org-wide default: a malformed settings blob or
a missing org row costs you a platform-shaped window, not a send.
4. How it differs from the platform fail-closed gates
This pattern is explicitly opt-in and is the mirror image of the always-on fail-closed rails Orbit does not let you toggle (the federal TCPA dialing window and the emergency routing rail — Plane A on the audit map). Those rails gate on input resolution and block outbound when the input fails closed; the defaults resolver gates on your opt-in and applies the platform constants when the opt-in is missing. The distinction matters when you read the toggle map: the map’s Plane B (tenant opt-in gates, tenant-owned defaults) is where this envelope sits. It is the second-order version of the model: not “which surfaces can I gate,” but “when I gate them, which defaults do my values feed to Plane C hygiene send-paths.”5. Caching and the observable shape
Resolution is cached for 60 seconds per tenant schema, in two parallel entries (one for erasure days, one for the quiet-hours pair). Operator settings change rarely; the cache keeps campaign send and erasure code from re-reading the org row on every send or scheduled delete. A bad tenant schema, a missing org row, or a database read error degrades to the platform constants with a warning log and a counter increment, and a Sentry-visible error capture so twenty-four-by-seven observability sees resolution failures rather than eating them. The strict-true check, the[0, 90] clamp for erasure, and the
[0, 23] clamp for hours are all applied after the cache lookup and
before the cache write, so nothing junk survives one bad read to
poison the cache.
Resolution flow
Related references
- Tenant posture audit map — Plane A/B/C narrative; this envelope sits in Plane B.
- Your Tenant Compliance Posture: The Toggle Map — the full inventory of tenant-owned toggles, including these two.
- Quiet hours configuration — the per-channel and per-campaign setting this envelope overrides.
- DSAR guide — Article-17 erasure handling, the request path the cooling-off default applies to.
- Compliance Health Scores — the early-warning snapshot you sample after changing these defaults.