walled.garden Schemas
walled.garden schemas define the shape of social data stored in Drives. They are Zod-backed, Standard Schema compliant, and exposed to template apps via nomad.schemas.
These schemas are content conventions — apps store walled.garden data wherever makes sense (there is no enforced directory path). The type string in each record identifies the schema.
person
Section titled “person”Identifies a Drive as a public social profile. Lives in the drive’s /index.json.
See the Profile Drives guide for full documentation.
{ "type": "walled.garden/person", "title": "Jane Doe", "description": "Bio here.", "thumb": "thumb.png", "links": [{ "label": "GitHub", "href": "https://github.com/janedoe" }]}A titled post — the building block of a forum or blog.
{ "type": "walled.garden/post", "title": "Hello world", "summary": "A short teaser shown in feed lists.", "body": "This is the full post content.", "category": "general", "tags": ["p2p", "hypercore"], "createdAt": "2026-06-15T12:00:00.000Z", "author": { "url": "hyper://profile/", "writerKey": "abc..." }}| Field | Type | Required |
|---|---|---|
type |
"walled.garden/post" |
✓ |
title |
String (max 280) | ✓ |
createdAt |
ISO 8601 datetime | ✓ |
summary |
String (max 560) | |
body |
String | |
category |
String (max 100) | |
tags |
Array of strings | |
draft |
Boolean | |
updatedAt |
ISO 8601 datetime | |
author.url |
Profile Drive URL | |
author.writerKey |
Hex string |
body is optional. When a post is stored as its own directory (/posts/<slug>/post.json), the body can instead live alongside it as post.md, post.html, or post.txt — the file extension determines how it renders, and the post stays readable even without a custom frontend (fetch it directly at /posts/<slug>/post.md). Consumers also read the legacy index.{md,html,txt} names from older posts. The body is deliberately not named index.*: on a fallback drive that would make the post directory resolve as a real page and shadow the app shell at the post’s canonical URL. draft: true hides a post from feeds and readers.
Declares a drive as a feed — a blog or other stream of items that a Reader can subscribe to. Lives in the drive’s /index.json.
{ "type": "walled.garden/feed", "title": "Ilya's Blog", "description": "Notes on peer-to-peer software.", "author": { "url": "hyper://my-profile/" }, "itemsPath": "/posts/", "itemType": "walled.garden/post", "language": "en", "icon": "icon.png"}| Field | Type | Required | Description |
|---|---|---|---|
type |
"walled.garden/feed" |
✓ | Identifies this drive as a feed |
title |
String (max 280) | ✓ | Channel title |
description |
String (max 1000) | Channel description | |
author.url |
Profile Drive URL | Identity behind the feed | |
itemsPath |
String | Directory holding items (default /posts/) |
|
itemType |
String | Schema type of items (default walled.garden/post) |
|
language |
String | BCP-47 language tag, e.g. en |
|
icon |
String | Path within the drive to a feed icon |
A blog is a feed whose items are posts: each post is a directory under itemsPath (e.g. /posts/2026-06-15-hello/) holding a post.json plus a post.md (or post.html/post.txt) body. See the Blog template.
comment
Section titled “comment”A reply to a post or any URL-addressable resource.
{ "type": "walled.garden/comment", "topic": "hyper://drive/posts/hello.json", "replyTo": "hyper://drive/comments/hello/prev.json", "body": "Great post!", "createdAt": "2026-06-15T12:05:00.000Z"}| Field | Type | Required |
|---|---|---|
type |
"walled.garden/comment" |
✓ |
topic |
URL of the resource being commented on | ✓ |
body |
String | ✓ |
createdAt |
ISO 8601 datetime | ✓ |
replyTo |
URL of the parent comment | |
updatedAt |
ISO 8601 datetime |
status
Section titled “status”A short broadcast, like a social media post.
{ "type": "walled.garden/status", "body": "Just shipped multi-writer support!", "createdAt": "2026-06-15T10:00:00.000Z"}bookmark
Section titled “bookmark”A saved or shared link.
{ "type": "walled.garden/bookmark", "href": "https://example.com/article", "title": "Great article", "description": "An optional note.", "tags": ["reading", "tech"], "createdAt": "2026-06-15T09:00:00.000Z"}follows
Section titled “follows”A list of Drive URLs the user subscribes to.
{ "type": "walled.garden/follows", "urls": ["hyper://abc.../", "hyper://def.../"]}follows is outbound-only, and lives in the follower’s private Root Drive (hyper://private/.data/walled.garden/follows.json) — it records the Drives you follow, and only your own Reader ever reads it. Following is subscriber-side: a Follow is you adding a URL here, not the target Drive recording a subscriber. There is deliberately no walled.garden/followers schema and no aggregate follower list — who follows a Drive is not published anywhere, so it can’t be enumerated at the data layer. Building a public followers list or count would defeat that; if you need a private-to-you follower registry, that’s a separate mechanism (an encrypted follow-inbox), not a published record. See ADR-0013.
Caveat (network layer): replicating any Drive announces your peer on that Drive’s DHT discovery topic, so a third party holding the Drive URL can observe connections to it regardless of Drive type. The data-layer privacy above is about no follower list existing; it is not a claim that replication is unobservable.
reaction
Section titled “reaction”An emoji or phrase reaction to a resource.
{ "type": "walled.garden/reaction", "topic": "hyper://drive/posts/hello.json", "phrases": ["thumbs up", "heart"]}An upvote or downvote on a resource.
{ "type": "walled.garden/vote", "topic": "hyper://drive/posts/hello.json", "vote": 1, "createdAt": "2026-06-15T11:00:00.000Z"}vote must be 1 (upvote) or -1 (downvote).
writer-keys
Section titled “writer-keys”A list of Autobase writer keypairs belonging to the same person. Published in a Profile Drive to enable multi-device identity resolution.
{ "type": "walled.garden/writer-keys", "keys": ["device1hex...", "device2hex..."]}Validating data
Section titled “Validating data”Use nomad.schemas.validate() to validate before writing:
var result = nomad.schemas.validate('walled.garden/post', postData)if (!result.success) { console.error(result.error)} else { await drive.put('/posts/my-post.json', JSON.stringify(result.data, null, 2))}About the namespace
Section titled “About the namespace”These schemas are inspired by unwalled.garden but are independently maintained and not compatible with the original spec. The walled.garden namespace signals that this is Nomad’s own evolution of those ideas.
