diff --git a/app/changelog/page.mdx b/app/changelog/page.mdx
index 3e65512..dde3e1e 100644
--- a/app/changelog/page.mdx
+++ b/app/changelog/page.mdx
@@ -21,6 +21,7 @@ This page lists changes since Working Draft 3. {{ className: 'lead' }}
- Switched from ISO 8601 to [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) for timestamps.
- In most cases, the two are interchangeable, but RFC 3339 is more strict. Most implementations should not need to change anything.
- Add optional `$schema` field to [Entities](/entities).
+- Added [Interaction Controls Extensions](/extensions/interaction-controls)
## Since WD 3
diff --git a/app/extensions/interaction-controls/page.mdx b/app/extensions/interaction-controls/page.mdx
new file mode 100644
index 0000000..baf6cc9
--- /dev/null
+++ b/app/extensions/interaction-controls/page.mdx
@@ -0,0 +1,108 @@
+export const metadata = {
+ title: "Interaction Controls Extension",
+ description: "Allows users to control who can interact with their Notes"
+}
+
+# Interaction Controls
+
+Often, it is desirable to post a Note, but control who is allowed to interact with it (e.g. send replies, like, dislike, etc.). This has traditionally not been possible in most federated networks: the Interaction Controls extension adds this possibility.
+
+## Usage
+
+The entity defined in this document must be inserted in the `pub.versia:interaction_controls` key of a [Note](/entities/note)'s extensions field.
+
+```jsonc {{ title: "Example Usage" }}
+{
+ "id": "456df8ed-daf1-4062-abab-491071c7b8dd",
+ "type": "Note",
+ "uri": "https://versia.social/notes/456df8ed-daf1-4062-abab-491071c7b8dd",
+ "created_at": "2024-04-09T01:38:51.743Z",
+ "content": {
+ "text/plain": {
+ "content": "Hello, world :happy_face:!"
+ }
+ },
+ "extensions": { // [!code focus:9]
+ "pub.versia:interaction_controls": {
+ "reply": {
+ "allowed": ["followers"],
+ },
+ }
+ }
+}
+```
+
+## Entity Definition
+
+
+
+
+
+ Describes permissions for a specific interaction.
+
+ ```typescript
+ type InteractionGroup = |
+ "everyone" |
+ "followers" |
+ "followed" |
+ "group" |
+ "mutuals";
+
+ type InteractionPermissions = {
+ allowed?: InteractionGroup[];
+ disallowed?: InteractionGroup[];
+ }
+ ```
+
+ Permissions can either be whitelist (`allowed` property) or blacklist (`disallowed` property). Both options are mutually exclusive.
+
+ In order of priority:
+ - `everyone`: Includes every single User in the federation.
+ - `followers`: Includes every follower of the author.
+ - `following`: Includes every User that the author follows.
+ - `mutuals`: Includes every mutual of the author (that is, every User that is both a follower and followed by the author).
+ - `group`: Includes every User in the [Group](/entities/group) that this Note was posted to, if any. If Note is not posted to a [Group](/entities/group), this value has no effect.
+
+ Permission groups are evaluated from highest to lowest priority: if two groups conflict each other, the group with the highest priority must be used.
+
+
+
+
+
+ ```jsonc {{ title: "Example"}}
+ {
+ "reply": {
+ "allowed": [
+ "group"
+ ],
+ },
+ "pub.versia:likes#Like": {
+ "disallowed": [
+ "everyone"
+ ]
+ }
+ }
+ ```
+
+
+
+## Usage
+
+### Interaction Types
+
+The following interaction types are defined as part of the core Versia spec:
+
+- `reply`: Sending a Note with `replies_to` including this Note.
+- `quote`: Sending a Note with `quotes` including this Note.
+
+Extensions **may** choose to register their own interaction types (such as `pub.versia:likes#Like` for the [Like Extension](/extensions/likes)). The naming scheme for interaction types is identical to [Extensions](/extensions)'s `type` property, but with a hashtag (`#`) in place of a forward slash (`/`).
+
+### Handling Permission Errors
+
+Implementations that find a user attempting to create an interaction they are not allowed to **MUST** return a `403 Forbidden` HTTP status code when processing the Note during federation. The Note **must** also be discarded.
+
+It is important for implementations to backfill any related [Collections](/structures/collection) (e.g. user followers) in order to not incorrectly reject Notes based off of outdated data.
+
+
+ To avoid server load from constant Collection refreshing, implementations **could** only refetch associated Collections when forbidden interactions are detected, then recalculate permissions again.
+
\ No newline at end of file
diff --git a/app/extensions/likes/page.mdx b/app/extensions/likes/page.mdx
index ee49ea4..6682831 100644
--- a/app/extensions/likes/page.mdx
+++ b/app/extensions/likes/page.mdx
@@ -104,4 +104,15 @@ The Likes extension adds the following collections to the [User](/entities/user)
"pub.versia:likes/Likes": "https://example.com/users/6e0204a2-746c-4972-8602-c4f37fc63bbe/likes",
"pub.versia:likes/Dislikes": "https://example.com/users/6e0204a2-746c-4972-8602-c4f37fc63bbe/dislikes"
}
-}
\ No newline at end of file
+}
+```
+
+## Interaction Types
+
+
+ This section only applies to implementors of the [Interaction Controls Extension](/extensions/interaction-controls).
+
+
+This extension registers the following interaction types:
+- `pub.versia:likes#Like`, for liking a Note
+- `pub.versia:likes#Dislike`, for disliking a Note
\ No newline at end of file
diff --git a/components/Navigation.tsx b/components/Navigation.tsx
index 062725f..9c00fd9 100644
--- a/components/Navigation.tsx
+++ b/components/Navigation.tsx
@@ -295,6 +295,10 @@ export const navigation: NavGroup[] = [
title: "Instance Messaging",
href: "/extensions/instance-messaging",
},
+ {
+ title: "Interaction Controls",
+ href: "/extensions/interaction-controls",
+ },
{ title: "Likes", href: "/extensions/likes" },
{ title: "Migration", href: "/extensions/migration" },
{ title: "Polls", href: "/extensions/polls" },