Skip to content

[Decision] What does turning publicSharing.enabled off mean for an ALREADY-MINTED share link? — the parent switch is mint-only while its own child predicate is now a standing policy #14033

Description

@os-steve

Split out of #13856 by the domain:services PM seat (#6021) at dispatch. #13856 keeps the unambiguous half (the redactFields fail-OPEN, wrong under either answer here) and is dispatched; this card is the policy question that half cannot answer.

⛔ Not mine to rule: it is a deployment-visible behaviour change on tokens already handed out, which is the same class the maintainer ruled explicitly for #13608 (ruling comment 5478574829) rather than letting a dev infer.

The question

ShareLinkService.getPolicy() returns an empty policy when the object's publicSharing block is absent or enabled !== true. Nothing in resolveToken() reads policy.enabled — the opt-in is checked only at mint (createLink refuses with SHARING_NOT_ENABLED).

Turning the object-level opt-in OFF does not stop anonymous serving of tokens already handed out.

Two defensible answers, and #13856's filer named both:

A — de-opt-in is a standing policy (links die). Symmetric with what #13608 just landed for eligibility.
B — de-opt-in is an authoring-surface switch (stops NEW mints only; existing links live on).

⭐ Why this is now sharper than when #13856 filed it

#13608 shipped on 2026-09-01 (PR #13857, fc9ba76a5). Its maintainer ruling made publicSharing.**eligibility** a standing policy, re-evaluated on every redemption, on the stated reasoning that the alternative is "a declared policy the platform does not hold".

eligibility is a child key inside the publicSharing block. So the platform now holds this shape:

declaration when enforced
publicSharing.eligibility (the predicate inside the block) mint and every redemption
publicSharing.enabled (the switch that governs the whole block) mint only

⇒ Tightening a predicate inside the block kills existing links, while turning the entire block off does not. An author who wants to stop anonymous serving would have to narrow the predicate rather than switch the feature off — the opposite of what the surface reads like.

⚠️ Stated as the argument it is, not as a ruling: this asymmetry is a reason to look, not proof that A is right. B is defensible on its own terms (an opt-out that silently breaks live links a customer is relying on is its own kind of harm, and unlike a predicate edit there is no per-record signal that anything changed).

Independent corroboration that the switch is load-bearing

The #13857 contract review reached the same seam from the contract side, unprompted, and recorded it as a scoped observation:

enforcement is governed by the parent publicSharing.enabled master switch — resolveToken applies no policy gate when the block is disabled, so a link minted through the documented system/permissive bypass (system-context.mdx ledger row 37), or minted while enabled and later orphaned by the author disabling the whole block, redeems without the eligibility re-check. This is pre-existing resolveToken structure, uniform across all sibling policy keys (redactFields likewise).

⭐ Two independent readings — the #13608 implementer and the contract reviewer — landed on the same structure from different directions. That is what makes it worth a ruling rather than a note.

⚠️ It also names a second reachable path worth deciding at the same time: a link minted through the system/permissive bypass carries no eligibility re-check either, because that too runs with the block treated as not-in-force.

What a ruling needs to settle

  1. A or B for enabled.
  2. If A: is it retroactive (every existing token on a disabled block stops resolving at deploy) or forward-only? sharing: publicSharing.eligibility is evaluated only at mint — a link keeps serving a record after it stops being eligible #13608 chose retroactive-and-immediate, and said so in its changeset as a breaking runtime change — the precedent exists but is not automatic.
  3. Whether the system/permissive-bypass mint path is in or out of whatever is decided.
  4. Uniformity across sibling keys is part of the answer, not a rider. The reviewer measured that redactFields sits under the same switch. sharing: turning publicSharing off leaves already-minted links serving — and silently drops the object's redactFields #13856 is fixing redactFields specifically because it is wrong under either answer; the ruling should say whether the remaining sibling keys follow enabled or their own rule, so the next key does not need its own card.

Not established

Related: #13856 (the redactFields half, dispatched) · #13608 / PR #13857 (the eligibility half, ruled and landed) · system-context.mdx ledger row 37 (the bypass path)

Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions