Skip to content

Feeds

This page shows how to use feeds: append-only logs owned by one peer, where each entry gets the next sequence number. A feed suits anything read in order and never edited: a blog, an activity log, a discussion thread.

Feed paths follow the document rules: letters, digits, -, _ and /, at most 256 characters.

err := cl.CreateFeed(ctx, "blog", "Ann's blog", "Notes on building things")

CreateFeed(ctx, path, title, description, opts...) creates a feed owned by you. Options:

Option Effect
WithCollaborative() Any peer may append, not only the owner.
WithFeedVisibility(v) Who may read. Feeds are public by default; pass wire.VisibilityShared or wire.VisibilityPrivate to restrict.
WithFeedServer(id) Send this request to one server only.

The title is at most wire.MaxFeedTitleLength (256) bytes and the description wire.MaxFeedDescriptionLength (4096).

The owner appends with AppendFeedEntry. Each entry has content bytes and an optional type tag (at most 64 bytes) that readers can filter on. The return value is the entry’s sequence number, starting at 1.

seq, err := cl.AppendFeedEntry(ctx, "blog", []byte("# Hello\n\nFirst post."), "post")

Another peer appends to a collaborative feed with AppendToFeed, which names the owner:

seq, err = other.AppendToFeed(ctx, ownerID, "blog", []byte("Nice post!"), "comment")

Appending to a feed that is not collaborative, as someone other than the owner, is client.ErrForbidden. The collaborative flag is about appending only: a private collaborative feed takes entries from anyone and shows them only to its readers.

The server caps the entries one feed may hold (storage.max_entries_per_feed, 10000 by default). An append at the cap is refused with status 507, as a full mailbox is.

entries, hasMore, err := cl.GetFeedEntries(ctx, ownerID, "blog",
client.WithFeedEntryFrom(1),
client.WithFeedEntryLimit(100),
client.WithFeedEntryType("post"),
)
for _, e := range entries {
fmt.Println(e.Seq, e.Type, e.CreatedBy, string(e.Content))
}
Option Effect
WithFeedEntryFrom(seq) First sequence number to return.
WithFeedEntryTo(seq) Last sequence number to return.
WithFeedEntryLimit(n) Page size. Server default 50, maximum 1000.
WithFeedEntryType(t) Only entries with this type tag.
WithFeedEntryServer(id) Send this request to one server only.

Each *client.FeedEntry has Seq, Type, Content, Hash (sha256: plus hex), CreatedAt (Unix milliseconds) and CreatedBy, the peer that appended it. To follow a feed, keep the last sequence number you saw and ask from the next one; hasMore says the page was cut short.

A missing feed makes GetFeedEntries return an error whose text is feed not found. It is not a typed client.ErrNotFound, so check GetFeed first if you need to tell the cases apart.

Read one entry, or the feed’s metadata:

entry, err := cl.GetFeedEntry(ctx, ownerID, "blog", 3) // nil, nil if absent
info, err := cl.GetFeed(ctx, ownerID, "blog") // nil, nil if absent
if info != nil {
fmt.Println(info.Title, info.CurrentSequence, info.Visibility)
}

ListFeeds(ctx, ownerID) returns the []client.FeedInfo of an owner’s feeds that you may read.

GetMultiFeedEntries fetches entries from several feeds, from any owners, in one request. Results are keyed by "<ownerPeerID>/<path>"; a feed you may not read reports its own Error instead of failing the call.

from := lastSeen + 1
results, err := cl.GetMultiFeedEntries(ctx, []client.BatchFeedQuery{
{OwnerPeerID: ann, Path: "blog", FromSequence: &from},
{OwnerPeerID: bob, Path: "blog"},
})
for key, r := range results {
if r.Error != "" {
log.Printf("%s: %s", key, r.Error)
continue
}
fmt.Println(key, len(r.Entries), r.HasMore)
}

Feeds take the same access calls as documents, on feeds you own:

_, err = cl.SetFeedVisibility(ctx, "blog", wire.VisibilityShared)
access, err := cl.GrantFeedReader(ctx, "blog", friendID)
_, err = cl.RevokeFeedReader(ctx, "blog", friendID)
access, err = cl.FeedAccess(ctx, "blog")

Visibility covers every entry. Making a feed private hides entries a peer has already read.

deleted, err := cl.DeleteFeed(ctx, "blog") // false, nil if it did not exist

Entries cannot be edited or removed one by one; deleting the feed removes all of them.

Feeds have no push notifications. Readers poll with WithFeedEntryFrom, or you pair the feed with a public mailbox for notifications, as in A public board.