> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Tenant compliance defaults — the opt-in envelope

> How Orbit gates per-tenant defaults for GDPR erasure cooling-off and campaign quiet hours: a master toggle plus one sub-toggle per feature, both boolean true, with platform constants otherwise. Fail-safe resolution, clamped values, and a 60-second cache.

# 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 value `true` 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.

<Warning>
  This page documents **tenant-owned** controls. Compliance for your
  traffic stays your responsibility; the envelope simply controls
  **which defaults** the resolver returns when your opt-in says to
  override. It is **not legal advice** — what window is adequate for
  your recipients is a question for counsel.
</Warning>

***

## 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.

The envelope exists so you can change those two org-wide defaults
without every other tenant inheriting the change by default. It is
deliberately harder to open than a normal settings page: you flip the
master toggle, then flip the sub-toggle for the specific feature,
then set the value on the organization record.

***

## 2. The opt-in envelope shape

The envelope lives on your organization `settings` object. Two levels
must both be `true` before the resolver honors your values:

```json theme={null}
{
  "compliance": {
    "tenant_defaults": {
      "enabled": true,
      "erasure": { "enabled": true },
      "quiet_hours": { "enabled": true }
    }
  }
}
```

The gates are **strict-true**: the resolver checks `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:

| Column                          | Meaning                                          | Bounds         |
| ------------------------------- | ------------------------------------------------ | -------------- |
| `gdpr_erasure_cooling_off_days` | GDPR Article-17 erasure cooling-off window, days | integer `0–90` |
| `default_quiet_hours_start`     | Quiet-hours window start hour                    | integer `0–23` |
| `default_quiet_hours_end`       | Quiet-hours window end hour                      | integer `0–23` |

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 reads `gdpr_erasure_cooling_off_days`, floors it to a whole
  number, and clamps it into `0–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 into `0–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.

The resolver reads four specific organization columns and `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](/concepts/tenancy-posture-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](/compliance/posture-overview): 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

```mermaid theme={null}
flowchart TD
    A[Request: resolve erasure or quiet hours] --> B{Cache hit<br/>(<60 s)?}
    B -->|Yes| Z[Cached value]
    B -->|No| C[Read org row: settings + columns]
    C --> D{tenant_defaults.enabled<br/>strict true?}
    D -->|No| P[Platform constant]
    D -->|Yes| E{feature sub-toggle<br/>strict true?}
    E -->|No| P
    E -->|Yes| F[Clamp value within bounds]
    F --> Z
```

***

## Related references

* [Tenant posture audit map](/concepts/tenancy-posture-audit-map) —
  Plane A/B/C narrative; this envelope sits in Plane B.
* [Your Tenant Compliance Posture: The Toggle Map](/compliance/posture-overview) —
  the full inventory of tenant-owned toggles, including these two.
* [Quiet hours configuration](/guides/quiet-hours-configuration) — the
  per-channel and per-campaign setting this envelope overrides.
* [DSAR guide](/compliance/dsar) — Article-17 erasure handling, the
  request path the cooling-off default applies to.
* [Compliance Health Scores](/compliance/compliance-health) — the
  early-warning snapshot you sample after changing these defaults.
