{
  "registry": "uiaf-storage-keys",
  "edition": "3.1.1",
  "description": "Canonical registry of retained UIAF browser storage. Retained storage only; transient values (the _uiaf_token URL parameter) are specified in the section 04 auxiliary contracts and are never storage rows. One base persistence predicate governs the uiaf_uid/uiaf_recovery pair everywhere: lifecycle resolved (or not_applicable under an established T0 policy), effective analytics_storage allowed, and a documented first-party analytics/persistent-identity purpose. Identity and attribution row semantics originate from the frozen master section 2.6 and are confirmed by Weave; purge-matrix semantics per effective-permission transition are confirmed by Harbor. ad_user_data and ad_personalization are routing/disclosure gates, not independent storage-retention gates. Lifecycle pending writes nothing. consent_record_id is payload receipt linkage only and is never browser storage.",
  "entry_metadata_fields": [
    "key",
    "mechanisms",
    "contents",
    "format",
    "max_size",
    "permission_gate",
    "ttl_renewal",
    "readers",
    "writers",
    "purge",
    "corruption_handling",
    "migration_version",
    "privacy_class",
    "requirement_ids"
  ],
  "keys": [
    {
      "key": "uiaf_uid",
      "mechanisms": [
        "cookie_server_set",
        "localStorage",
        "sessionStorage"
      ],
      "contents": "Persistent user identifier {uuid_v4}.{unix_seconds}",
      "format": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\\.[0-9]{9,11}$",
      "max_size": "64 bytes",
      "permission_gate": "base_persistence_predicate",
      "ttl_renewal": "Cookie Max-Age 34560000 as requested by the server; no fixed-lifetime outcome is promised. Server-side refresh only; client code never rewrites a healthy matching cookie and never overwrites a divergent valid cookie (adopt + update script storage only). The client Web Storage sync writer persists the LS/SS mirrors ONLY after the mandatory authoritative cookie re-read; cookie writes themselves remain server-only.",
      "readers": [
        "client_init",
        "middleware",
        "cookie_endpoint"
      ],
      "writers": [
        "cookie_endpoint",
        "middleware",
        "client_web_storage_sync_after_authoritative_cookie_reread"
      ],
      "purge": "Deleted on analytics-permission loss, revocation, erasure, and dormant transition; atomically with uiaf_recovery.",
      "corruption_handling": "Invalid value treated as absent; invalid inbound cookie expired via Max-Age=0.",
      "migration_version": "v3-initial",
      "privacy_class": "pseudonymous_identifier",
      "requirement_ids": [
        "UIAF-04-AUX-001"
      ]
    },
    {
      "key": "uiaf_recovery",
      "mechanisms": [
        "localStorage",
        "sessionStorage"
      ],
      "contents": "Opaque server-bound recovery credential (bound to UID, deployment/tenant, issuance version, expiry, recovery policy)",
      "format": "opaque bounded non-semantic string",
      "max_size": "512 bytes",
      "permission_gate": "base_persistence_predicate",
      "ttl_renewal": "Server-side expiry/revocation/rotation mandatory; returned only over TLS with Cache-Control: no-store.",
      "readers": [
        "client_recovery_path"
      ],
      "writers": [
        "client_after_cookie_endpoint_response"
      ],
      "purge": "Purged atomically with uiaf_uid on every applicable path. Never a cookie. Never logged raw. Never DSR authentication.",
      "corruption_handling": "Treated as absent; recovery unavailable.",
      "migration_version": "v3-initial",
      "privacy_class": "credential_reference",
      "requirement_ids": [
        "UIAF-04-AUX-002"
      ]
    },
    {
      "key": "uiaf_session_state",
      "mechanisms": [
        "sessionStorage"
      ],
      "contents": "Sole v3 session representation: {session_id, next_seq}",
      "format": "JSON object {session_id: uuid_v4, next_seq: integer 0..4294967295}",
      "max_size": "128 bytes",
      "permission_gate": "purpose_storage_gated_session",
      "ttl_renewal": "Session lifetime; new UUID-v4 session with next_seq 0 on rollover, corruption, oversize, or allocator rekey.",
      "readers": [
        "client_allocator"
      ],
      "writers": [
        "client_allocator"
      ],
      "purge": "Purged on analytics-purpose loss, revocation, erasure, and dormant transition, with other session state.",
      "corruption_handling": "Corrupt/oversized state creates a new session.",
      "migration_version": "v3-initial",
      "privacy_class": "ephemeral_identifier",
      "requirement_ids": [
        "UIAF-04-SESS-001"
      ]
    },
    {
      "key": "uiaf_attribution",
      "mechanisms": [
        "localStorage",
        "sessionStorage"
      ],
      "contents": "Attribution record: first_touch/last_touch touchpoints (with required touch_id, per-value click-ID captured_at/expires_at metadata), count",
      "format": "JSON; touchpoint canonical form at most 6144 UTF-8 bytes; click-ID object at most 4096; custom object at most 2048",
      "max_size": "16384 bytes practical",
      "permission_gate": "effective analytics_storage allowed (click-ID members additionally require effective ad_storage allowed)",
      "ttl_renewal": "Expired click-ID values (now >= expires_at) removed from live stores and omitted at assembly before body freeze.",
      "readers": [
        "client_attribution"
      ],
      "writers": [
        "client_attribution"
      ],
      "purge": "Click-ID members stripped on effective ad_storage loss; whole attribution record deleted on analytics-permission loss, revocation, erasure, dormant transition. No attribution backup cookie exists. Legacy mechanism purge: the retired pre-3.0 uiaf_attribution COOKIE is never read or migrated and is expired on sight (Max-Age=0) wherever encountered.",
      "corruption_handling": "Malformed JSON treated as absent (storage loss mints, never reconstructs, touch identifiers).",
      "migration_version": "v3-initial",
      "privacy_class": "attribution_record",
      "requirement_ids": [
        "UIAF-04-ATTR-001"
      ]
    },
    {
      "key": "uiaf_retry_queue",
      "mechanisms": [
        "localStorage"
      ],
      "contents": "Retry entries keyed by immutable event_id: {payload (immutable frozen bytes), enqueued_at, attempts, next_attempt_at, bytes, required_permissions (immutable), purpose (immutable), destinations (immutable)}. required_permissions, purpose, and destinations are frozen at enqueue time with the body and are never recomputed, widened, or edited afterward.",
      "format": "JSON map keyed by event_id",
      "max_size": "byte and count bounded (deployment-configured); 72h TTL; attempt cap",
      "permission_gate": "Storage permitted only when device storage is allowed for the event's purpose; otherwise failed delivery is honestly lost.",
      "ttl_renewal": "Bounded exponential backoff with jitter sets next_attempt_at; 72-hour TTL; terminal outcomes remove entries individually.",
      "readers": [
        "client_drain_web_locks_or_lease"
      ],
      "writers": [
        "client_delivery"
      ],
      "purge": "On capability downgrade, entries whose immutable required_permissions are no longer satisfied are dropped before send (never redacted/resent under the same event_id); still-permitted entries are retained. A frozen body containing any click-ID occurrence past expires_at is terminally dropped whole. Cleared on revocation, erasure, dormant transition.",
      "corruption_handling": "Malformed/quota-failed storage degrades to honest loss.",
      "migration_version": "v3-initial",
      "privacy_class": "event_buffer",
      "requirement_ids": [
        "UIAF-04-RETRY-001"
      ]
    },
    {
      "key": "uiaf_dirty_baseline",
      "mechanisms": [
        "sessionStorage"
      ],
      "contents": "The canonical dirty-projection string (UTF-8 RFC 8785 JCS over the exact closed projection tree), not a hash",
      "format": "JCS string, at most 8192 UTF-8 bytes",
      "max_size": "8192 bytes",
      "permission_gate": "Written only when its purpose and storage permission allow.",
      "ttl_renewal": "Advances only after a 2xx acknowledgement or after the immutable event is durably queued.",
      "readers": [
        "client_dirty_check",
        "hydration_comparison"
      ],
      "writers": [
        "client_commit_path"
      ],
      "purge": "Purged on every applicable downgrade/revocation/erasure path; a capability transition changes the projection before the stripped session is evaluated.",
      "corruption_handling": "Corrupt or over-8192-byte content treated as absent with one bounded diagnostic (at most one extra send).",
      "migration_version": "v3-initial",
      "privacy_class": "state_projection",
      "requirement_ids": [
        "UIAF-04-DIRTY-001"
      ]
    }
  ],
  "legacy_purge_only": {
    "description": "Maestro security migration-hardening ruling: the unique, lexicographically ordered, CLOSED purge-on-sight set of pre-3.0 residue keys. Never read, migrated, or written as live v3 state; cleared on sight across cookie and Web Storage mechanisms. Occurrences outside retirement/negative-test/superseded-history contexts fail conformance. Docs (07/08/tests) must render or defer to this source, never hand-maintain a competing list. The pre-3.0 uiaf_attribution COOKIE is not listed here because uiaf_attribution remains a live v3 Web Storage row: its legacy cookie-mechanism purge is recorded in that row's purge metadata.",
    "keys": [
      "uiaf_last_payload_hash",
      "uiaf_pre_click_ids_enc",
      "uiaf_pre_landing_url",
      "uiaf_pre_referrer",
      "uiaf_pre_timestamp",
      "uiaf_pre_utms",
      "uiaf_session",
      "uiaf_session_id"
    ]
  },
  "absent_by_design": {
    "description": "Mechanisms the registry explicitly does not contain.",
    "items": [
      "attribution backup cookie",
      "uiaf_consent_epoch",
      "uiaf_optout",
      "_uiaf_token as storage (transient URL parameter only; contract in section 04 auxiliary interfaces)"
    ]
  }
}
