Skip to content

Visibility and rules

This page explains what users are allowed to interact with, what Directus enforces, and what still belongs in application code.

The visibility rules are not just technical constraints. They are there to keep the social behavior of the product predictable.

Target visibility matrix

Target Allowed interaction state Comments allowed Reactions allowed Enforced where
books status = published Yes Yes Directus flows
reading_journeys visibility = public and status in (active, finished) Yes Yes Directus flows
reading_shares visibility = public and status = published Yes Yes Directus flows
reading_shares with private or non-published state Not interactable for general users No No Directus flows

Why the interaction gates are different

  • Books are treated as public reference content, so the main gate is publication state.
  • Reading journeys can be personal, so both visibility and status matter.
  • Reading shares behave like published posts, so they need to be both public and published before other members can interact.

This keeps private or unfinished member activity from accidentally becoming social content.

Comment visibility states

Only one comment collection currently carries its own extra visibility flag.

Collection Visibility field Allowed values Meaning
reading_share_comments visibility public, share_author_only A share comment can be fully public or restricted
reading_journey_comments none n/a Journey comments currently follow target-level visibility only
book_comments none n/a Book comments currently follow target-level visibility only

This difference is intentional. Reading share comments already needed a more nuanced visibility option, while book and journey comments are currently kept simpler to reduce complexity in both moderation and read filtering.

Important boundary: share_author_only

This is the one rule that is not fully solved inside Directus.

  • The schema state exists in reading_share_comments.visibility.
  • Directus write guardrails protect comment creation on invalid share targets.
  • Final read filtering for share_author_only still belongs in the app or API layer.

That means the CMS can store the state safely, but public listing endpoints still need to decide what to hide.

We left this split in place because viewer-specific read filtering is awkward to express globally in Directus. The app knows who is asking for the data, so it is the better place to decide what that viewer should or should not see.

Ownership rules

Collection group Owner field Can clients set it directly? Actual source of truth
reading_journeys, reading_shares user Typically app-managed App logic
All comment collections user No Directus create flows overwrite it from the authenticated user
All reaction collections user No Directus create flows overwrite it from the authenticated user

In practice, user_created is not a substitute for the business owner field. The docs and flows treat user as the content owner.

This protects one of the most important trust boundaries in the system: members should not be able to create content on behalf of someone else just by sending a crafted payload.

Delete behavior

Relation Delete behavior Why
Interaction target -> comments CASCADE Deleting the target should remove attached discussion records
Interaction target -> reactions CASCADE Reactions have no meaning after the target is gone
User -> comments SET NULL Preserve discussion content if an account is deleted
User -> reactions SET NULL Preserve aggregate history even if identity is gone

The delete rules balance cleanup against preservation.

  • If the target disappears, the interaction should disappear too.
  • If the user disappears, the conversation or signal may still be worth keeping.

That is why target relations use CASCADE, while owner relations use SET NULL.

Moderation and status fields

Collection Status field Values
books status published, archived
authors status published, archived
reading_journeys status active, finished, archived
reading_shares status draft, published, archived
reading_share_comments status published, hidden, archived
reading_journey_comments status published, hidden, archived
book_comments status published, hidden, archived

Reaction collections do not carry a status field. If moderation becomes necessary later, that would be a deliberate model change, not a hidden feature waiting to be turned on.

This was a conscious tradeoff. Reaction records are meant to stay cheap and simple. If we added a moderation workflow to them too early, we would add operational cost to the lightest interaction in the system.

Practical reading of the rules

If you are building against this model, the safe assumptions are:

  1. A comment or reaction row should never be trusted just because the client submitted it.
  2. The target record's status and visibility decide whether interaction is allowed.
  3. Share-comment visibility has an extra application-side read rule because of share_author_only.
  4. Reaction rows represent a single like, not a flexible emoji or multi-state reaction system.

The broader design goal is simple: make public interaction easy, make private content safe, and keep the rules understandable enough that developers and editors can reason about them without guessing.