Skip to content

Decide the shared masked member identifier for private management surfaces #288

Description

@alexeygrigorev

Parent epics: #54 and #108. Split from the shared management-mask gap recorded in #245 and #250.

PM status — owner decision required

FAIL CLOSED. Do not engineer while decision and needs grooming remain.

Choose one default non-PII member label for every authorized private Studio/admin API course-registration, Enrollment, MemberProfile, operation, audit-safe presentation, and export surface. #245 and #250 must consume the same choice; neither may invent a separate email/name mask or reuse the event-provider helper.

This issue is only a product-presentation decision. It does not authorize a route, data read, export, mutation, schema change, production/provider access, or PII disclosure.

Normative authority and fixed invariants

Every option below retains these invariants:

  1. The only input is an already-authorized MemberProfile UUID. Email, normalized email, name, certificate/display alias, organization, profile values, content.Person, provider identifiers, and operator input are never inputs.
  2. Parse the UUID, then canonicalize it as exactly 32 lowercase hexadecimal characters with hyphens removed. Invalid, absent, quarantined, colliding-identity, broken/cyclic/absorbed, inactive, deleted, unauthorized, and nonexistent subjects expose no label and preserve the consuming service's generic non-enumerating result.
  3. The label is presentation only. The canonical full profile UUID remains the resource identity. A label is never accepted as a route identifier, lookup/search/filter key, idempotency key, authorization input, correlation key, or uniqueness proof.
  4. A truncated-label collision never reveals PII, extends only one subject's label, changes the grammar dynamically, or changes denial behavior. Colliding labels remain identical; resource UUIDs and authorized object scope distinguish resources.
  5. Labels are private, zero-TTL, no-store, noindex, absent from public pages/search/sitemaps/analytics. They do not grant PII access and do not widen the authorized queryset.
  6. Ordinary application logs, metrics, traces, errors, job context, and provider payloads do not record the label. Append-only management audit may store the selected label only as its already-authorized redacted target-label snapshot beside target type/opaque UUID; denied/unknown targets receive no label. If the final audit registry is stricter, it stores the opaque UUID only—never a fallback email/name/digest.
  7. HTML renders the exact literal text rather than CSS-only truncation. Its accessible name is Member identifier, starts <spaced prefix>, ends <spaced suffix> for truncated options, so the ellipsis and hexadecimal characters are not the sole spoken distinction. Labels remain text at zoom/reflow, are not color-only, and copy/links/actions keep a distinct accessible name. API and CSV serialize the exact visible string or null, never a different mask.

Exact candidate formats

Only the following existing-UUID formats are in this decision. They need no new identity input, persistent alias, or masking secret.

Option A — compact UUID edge label (recommended)

Member <first 4 hex>…<last 4 hex>

Use the literal prefix Member and one Unicode horizontal ellipsis (U+2026), with no surrounding spaces. Example UUID 2f910000-0000-4000-8000-0000a1b2c3d4 renders:

Member 2f91…c3d4

Accessible name:

Member identifier, starts 2 f 9 1, ends c 3 d 4

Consequences:

  • exposes 32 bits of the already-authorized opaque UUID; stable for the lifetime of that profile and unaffected by email, name, profile-field, account-state, or course changes;
  • compact at mobile/reflow sizes and relatively short for screen-reader repetition;
  • truncated labels can collide. The UI/API/CSV must tolerate identical labels and retain the full authorized resource UUID separately; no adaptive suffix or PII fallback is allowed;
  • remains linkable across authorized private surfaces, so it is excluded from public output and ordinary observability despite containing no email/name characters.

Option B — extended UUID edge label

Member <first 6 hex>…<last 6 hex>

The same example renders:

Member 2f9100…b2c3d4

Accessible name:

Member identifier, starts 2 f 9 1 0 0, ends b 2 c 3 d 4

Consequences:

  • exposes 48 bits of the opaque UUID and is stable under the same conditions as Option A;
  • lowers accidental label-collision risk relative to Option A but still is not a uniqueness proof and retains exactly the same collision behavior;
  • is longer and more repetitive in dense tables, exports, zoom/reflow, and spoken output;
  • has greater cross-surface linkability than Option A and therefore retains the same private/no-store/logging restrictions.

Option C — full opaque UUID label

Member <canonical hyphenated lowercase UUID>

The same example renders:

Member 2f910000-0000-4000-8000-0000a1b2c3d4

Accessible name spells the UUID in its five canonical groups after Member identifier.

Consequences:

  • has no truncation collision beyond the authoritative resource identity and requires no second identifier beside the label;
  • exposes the complete stable opaque resource identifier and maximizes cross-surface linkability;
  • is cumbersome in tables, mobile layouts, CSV review, copying, and repeated screen-reader output;
  • is not recommended for the requested masked presentation, although it remains non-PII under current authority. It retains all private/no-store/logging restrictions.

Recommended safe default

Recommend Option A. It uses only the immutable authorized profile UUID, introduces no email/name disclosure, normalization policy, persistent alias, new secret, rotation lifecycle, or model field, and is the smallest consistent presentation across #245 and #250. Its collision tradeoff is acceptable only because the label is explicitly non-unique and never the resource or lookup key.

This is a recommendation, not approval.

Explicitly excluded formats

The following are not candidate defaults and require a new owner-reviewed decision if requested:

  • partial or hashed email, domain, name, certificate/display alias, profile text, or combinations of them;
  • the existing protected:<12 hex> helper, which is scoped to an external event-provider identifier and is not member-identity authority;
  • unkeyed hashes, reversible/encrypted values, stable recipient digests, or provider identifiers;
  • keyed/HMAC aliases, which would add secret ownership, rotation, versioning, historical audit/export behavior, and outage semantics not authorized here;
  • random stored aliases, which would add schema, uniqueness, migration, lifecycle, restoration, and deletion behavior not authorized here;
  • per-page ordinals or unstable masks, which cannot safely correlate list/detail/export/operation results;
  • any fallback from a missing/invalid/unauthorized profile UUID to PII or another identifier.

Downstream contract after owner approval

After an authorized owner selects an option and PM records it:

  • Add target-native course registration and enrollment reads and exports #245 uses one nullable member_label with the exact shared grammar for masked CourseRegistration and Enrollment Studio rows/details, admin API representations, operation summaries, and CSV columns. Its current default masked account identifier / masked normalized-email snapshot wording must be replaced rather than implemented alongside the shared label. Full account UUID/email remain separately capability-gated PII fields. Missing/unavailable labels serialize as null in API/CSV and display Hidden in Studio only where the consuming issue already distinguishes masked from genuinely absent data.
  • Add capability-scoped Member Studio and admin correction and Slack resend #250 uses the same nullable member_label for Members list/detail and safe correction/resend review/result presentation. The full authorized profile UUID remains the route/API resource ID. Full PII remains separately gated by accounts.member_profile.view_pii.
  • Shared operations and permitted audit target-label snapshots use the same grammar; adapters and exports may not fork it.
  • Both issues add collision fixtures, null/unavailable/non-enumerating fixtures, exact API/CSV/HTML parity assertions, redaction canaries, and desktop/mobile keyboard/screen-reader/zoom/reflow evidence. Approval of this label does not satisfy either issue's other dependency or lifecycle gates.

Until that reconciliation is complete, #245 and #250 remain decision / needs grooming; #246 may not finalize management result presentation before accepted #245.

Owner response required

An authorized product/security owner should reply with exactly one of:

APPROVE #288 OPTION A — COMPACT UUID EDGE LABEL
APPROVE #288 OPTION B — EXTENDED UUID EDGE LABEL
APPROVE #288 OPTION C — FULL OPAQUE UUID LABEL

or provide an exact replacement covering input, canonicalization, literal grammar, examples, collision behavior, missing/unavailable identities, stability/rotation, privacy/linkability, accessibility, logging/audit, and downstream API/CSV/HTML fields.

Decision acceptance and non-goals

Non-goals: implementing accounts/profile services; changing #247 identity/schema; permissions or PII capability work; course/member reads or exports; operation/audit infrastructure; support correction; Slack resend; privacy-rights workflows; routes/UI/API/OpenAPI; tests; migrations; deployment; production/provider/secret/data access; or changing public content.Person behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Must-have or release-blockingadminArea: adminauthArea: authcoursesArea: coursesdecisionOwner decision requiredneeds groomingRaw intake awaiting PM groomingsecurityArea: security

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions