Activity · Visibility
Visibility and host-owned policy
Activity enforces reusable feed visibility from the signed tenant and actor context. Your host still decides whether a user may publish, follow, block, report, or act as a particular actor before it signs the request.
Host authorization
└─ may this user perform this product action?
Activity enforcement
└─ is this post visible to this signed actor now? Activity cannot recover business roles, subscription state, organization membership, or object permissions from an actor_id.
Post visibility
Set visibility when publishing a post:
| Value | Author | Active follower | Other actor in tenant | Follower fan-out |
|---|---|---|---|---|
public | Visible | Visible | Visible | Yes |
followers | Visible | Visible | Not visible | Yes |
private | Visible | Not visible | Not visible | No |
Visibility applies after tenant isolation. A public post is public inside its Activity tenant, not across tenants.
The current post update contract can change body, metadata, and render_refs. It does not expose a visibility transition. Choose visibility at publish time.
Profile and home reads
The home timeline reads materialized entries for the signed actor, then applies current deletion, moderation, hide, mute, and block filters.
The profile endpoint evaluates visibility when it reads the source posts:
GET /api/v1/actors/{actor_id}/posts An author can read their own visible posts. Another actor sees public posts plus follower-only posts when an active follow exists. Private posts remain author-only.
Both endpoints return an empty result when no posts are visible. A post-level action that cannot see its target returns post_not_found, so the response does not disclose whether a hidden or inaccessible post exists.
Follow and unfollow
Following creates or reactivates an edge from the signed actor to followed_id:
POST /api/v1/follows
Content-Type: application/json
{ "followed_id": "actor-b" } The edge enables follower visibility and future follower fan-out. It does not backfill older posts into the home timeline immediately.
Unfollow deletes the edge and removes existing following entries from that author’s posts. It does not delete the source posts.
Mute
Mute changes the caller-to-target relationship state to muted and removes that target’s existing entries from the caller’s timeline:
POST /api/v1/relationships/{actor_id}/mute Muted posts are also excluded from profile reads by the muted viewer. Mute is directional. It does not prevent the other actor from reading or following the caller.
The current unmute operation restores the relationship state to active:
DELETE /api/v1/relationships/{actor_id}/mute That is the current contract, even when the relationship was not previously an active follow. Consumers should not model mute as an independent preference flag.
Block
Block creates a blocked relationship from the caller to the target. Activity then:
- removes existing timeline entries between both actors
- prevents either direction from reading the other’s profile posts
- prevents new follows while either direction has a block edge
- excludes future follower fan-out across the blocked pair
POST /api/v1/relationships/{actor_id}/block Unblock deletes the caller’s block edge. It does not restore previous follows or timeline entries:
DELETE /api/v1/relationships/{actor_id}/block Hide
Hide is scoped to one viewer and one post:
POST /api/v1/posts/{post_id}/hide Activity records the hidden pair and removes the post from that viewer’s materialized timeline. Other viewers and the source post are unchanged. A rebuild continues to exclude the hidden post.
The hidden pair has a database uniqueness constraint. After hiding, the post is no longer visible to that viewer, so repeating the endpoint returns post_not_found instead of creating another record. The public contract does not currently expose an unhide operation.
Report and moderation
Report records one report per tenant, post, and actor:
POST /api/v1/posts/{post_id}/reports
Content-Type: application/json
{
"reason": "host-policy-code",
"metadata": { "case_id": "case_1842" }
} Repeating the report updates its reason and metadata. Activity also writes a post.reported outbox event.
Reporting does not hide the post, change moderation_state, or decide a moderation outcome. The current public API has no moderation transition, review queue, report export, restore, or webhook delivery contract. The host owns those decisions and workflows.
Responsibility matrix
| Decision or state | Owner |
|---|---|
| Which tenant and actor the caller may represent | Host |
| Whether the user may publish or follow | Host |
| Post visibility enum and graph enforcement | Activity |
| Hide state for one viewer and post | Activity |
| Report record and outbox event | Activity |
| Report review and moderation decision | Host |
| Product ranking and recommendation policy | Host |
| Rendering host objects and media references | Host |
Current gaps
Protected accounts and follow requests are not modeled. Visibility cannot be changed after publish through the public API. Reports have no public review or delivery workflow. The moderation state field exists, but hosts cannot transition it through /api/v1.
Treat these as product gaps, not implied extension points.