Create
Create makes a new object. Dispatch is by activity.objectType, looked up in the handler's MODELS map: Bookmark, Circle, Group, Page, Post, React, Reply, User.
Required (beyond the envelope schema): objectType (must be a MODELS key), object (an object), to (a string), canReply and canReact (both must be defined, any type). For objectType: "User": object.password (or the legacy object.pass) is required, plus either object.username or object.actorId/object.id.
{ "type": "Create", "objectType": "Post", "to": "@public", "canReply": "@public", "canReact": "@public", "object": { "type": "Note", "content": "Hello world", "tags": ["intro"], "location": { "name": "SF", "lat": 37.77, "lon": -122.41 }, "attachments": [{ "fileId": "file:65f...@kwln.org" }], "featuredImage": "file:65f...@kwln.org" }}Post types: Note, Article, Link, Media, Event.
- For
Event, sendobject.startTime/object.endTime(ISO strings) -- the handler maps these intoobject.event.startDate/endDate. - For
Link(sharing another Kowloon post), sendobject.target= the shared post's ID. The server resolvestargetActoritself fromFeedItems-- a client-senttargetActoris always stripped, since it's a third-party attribution claim that must not be client-trusted.
Circle
Section titled “Circle”{ "type": "Create", "objectType": "Circle", "to": "", "canReply": "", "canReact": "", "object": { "type": "Circle", "name": "Close Friends", "summary": "inner circle", "icon": "file:..." }}to defaults to the creator's own actorId if blank (owner-scoped). canReply/canReact are always force-set to mirror to regardless of what's sent -- not independently meaningful yet, kept for a hypothetical future circle-comments feature.
{ "type": "Create", "objectType": "Group", "to": "@public", "object": { "type": "Group", "name": "Book Club", "summary": "...", "rsvpPolicy": "serverOpen", "location": {} }}rsvpPolicy drives Join approval logic: open, serverOpen, serverApproval, approvalOnly, inviteOnly.
Creating a Group auto-creates 5 system Circles (Admins/Moderators/Members/Blocked/Pending) via the model's pre-save hook -- the handler re-fetches the doc afterward because those circle IDs aren't present on the pre-save return value. The creator is also added to their own circles.groups System circle.
Bookmark / Folder
Section titled “Bookmark / Folder”{ "type": "Create", "objectType": "Bookmark", "object": { "type": "Bookmark", "title": "Cool site", "href": "https://example.com", "parentFolder": "bookmark:...@kwln.org" }}type: "Folder" records omit href. Max folder depth is 2, enforced at create time.
{ "type": "Create", "objectType": "Page", "object": { "type": "Page", "title": "About", "content": "markdown body...", "slug": "about" }}User (registration)
Section titled “User (registration)”{ "type": "Create", "objectType": "User", "object": { "username": "alice", "password": "hunter2improved" }}This is the only unauthenticated POST /outbox path (see Overview). activity.actorId is forced to the server actor by routes/outbox/post.js.
Side effects (all types except User)
Section titled “Side effects (all types except User)”activity.object.actorId/.actorpopulated from the authenticated actor if missing.- Markdown content is stripped of raw HTML (only
<u>/<s>allowed) viastripHtmlFromMarkdown;source.mediaTypeis forced totext/markdown. - Addressing (
to/canReply/canReact) normalized to the canonical scheme. locationnormalized to GeoJSON{ type: "Point", coordinates: [lng, lat] }.featuredImage->image; attachment objects -> bare file-ID strings.- Referenced
file:IDs getFile.parentObjectback-linked for visibility inheritance (only if not already set). writeFeedItems(created, type)-- fans the object into theFeedItemstimeline cache. This is what makes it show up inGET /postsetc. -- a directModel.create()skips this (see Architecture).- Post creation increments
User.postCount; Circle creation triggers async mosaic-icon regeneration; new Groups add the group to the creator'scircles.groupsSystem circle. - Notifications:
new_postfeed nudges (throttled 12h, opt-in) +@mentionnotifications for local users tagged in the body -- both fire-and-forget. - Fire-and-forget: external
og:imageURLs on Posts get proxied into local storage.
Response
Section titled “Response”{ activity, created: <full Model doc as plain object>, federation }. On a DB unique-key collision (E11000, from a duplicate submit), returns the existing document instead of erroring.
Any authenticated user (the Create -> User registration path is the unauthenticated exception). No ownership check is needed -- you're creating your own object.
Client mapping
Section titled “Client mapping”createPost, createCircle, createGroup, createBookmark, createPage all map 1:1 to this shape. Field names match -- the client sends content/href/featuredImage/tags at the top level of object, matching what the handler expects.