Docs Menu

Build a Bonafide App

Getting Started

Build an app on Bonafide. A reference implementation exists, and it powers Pools in production.

Bonafide has a working reference implementation, and Pools runs on it in production. This guide explains what's involved in building an app of your own.

If you'd rather build something with us, we're happy to help. Build something with us →

What Bonafide handles

When people interact through an app, a few things have to work. They need to establish connections, control what they share, and exchange information reliably. In a decentralized system, that information also has to move between independent databases rather than living in one shared store.

Bonafide handles much of this at the protocol level: connections, access control, encrypted communication, propagation, and persistence. As a developer, you define the information your app works with and what people can do with it. The protocol and reference library handle much of the underlying machinery.

What exists today

  • A reference implementation. The bonafide-agent library implements the protocol, alongside a router service. Both are used by Pools in production.
  • A published specification. The Bonafide Protocol spec describes how everything works. You don't need to read it cover to cover to start building. The library exposes a smaller API, and the specification is there when you need the technical details.

We're also working on a create-bonafide-app template and an automated conformance test suite. Neither is available yet.

Here's what building an app involves.

1. Use an existing domain, or define your own

A domain defines the information that apps can exchange and the rules for working with it.

If you're building something that fits an existing domain, you can use its vocabulary. That also gives your app a way to interoperate with others implementing the same domain.

If you're building something new, you'll need to define its content types, fields, relationships, and operations.

The spec's content model chapter explains how this works. The Pools domain vocabulary is a complete example from an app already in production.

2. Register your schema and handlers

Your app registers its content types with the agent library and provides handlers for the four message types: connection, subscription, content, and command.

Here's an example of how a domain is registered using the reference library.

import {
  registerNamespace,
  registerContentValidator,
  registerMessageHandler,
  MESSAGE_HEADER_TYPE_CONNECTION,
  MESSAGE_HEADER_TYPE_SUBSCRIPTION,
  MESSAGE_HEADER_TYPE_CONTENT,
  MESSAGE_HEADER_TYPE_COMMAND
} from '@protegra/bonafide-agent-7';
import myDomainContentTypes, { MY_DOMAIN_NAMESPACE } from './namespace/my-domain-schema.js';
import { handleConnectionMessage } from './services/connection-handlers.js';
import { handleSubscriptionMessage } from './services/subscription-handlers.js';
import { handleContentMessage } from './services/content-handlers.js';
import { handleCommandMessage } from './services/command-handlers.js';

export function registerMyDomain() {
  // Tell the library what content types your domain defines
  registerNamespace(MY_DOMAIN_NAMESPACE, myDomainContentTypes);

  // Wire up the four message types every domain handles
  registerMessageHandler(
    MESSAGE_HEADER_TYPE_CONNECTION,
    (context, cxn, operation, messageContent) =>
      handleConnectionMessage(context, cxn.cxnId, operation, messageContent)
  );
  registerMessageHandler(
    MESSAGE_HEADER_TYPE_SUBSCRIPTION,
    (context, cxn, operation, messageContent) =>
      handleSubscriptionMessage(context, cxn.cxnId, operation, messageContent)
  );
  registerMessageHandler(
    MESSAGE_HEADER_TYPE_CONTENT,
    (context, cxn, operation, messageContent) =>
      handleContentMessage(context, cxn.cxnId, operation, messageContent)
  );
  registerMessageHandler(MESSAGE_HEADER_TYPE_COMMAND, handleCommandMessage);

  // Tell the library how your content types participate in access control
  registerContentValidator(MY_DOMAIN_NAMESPACE, {
    subbables: ['topLevelThing'],       // own `ck` is the subscription label
    subbableRelated: ['memberThing'],   // travels under its parent's label
    unshareables: [],                   // never shared under any subscription
    subUnrelated: ['alias'],            // outside the subscription system entirely
    getParentCK: content => content.topLevelThingCK ?? null
  });
}

The content validator tells Bonafide how your content types relate to subscriptions and access control.

Category What it means Example
subbables Content that can be subscribed to directly. Its own key becomes the subscription label. A pool, a thread
subbableRelated Content shared under its parent's subscription label. A membership record
unshareables Content that isn't shared through subscriptions.
subUnrelated Content handled outside the subscription system. alias

getParentCK tells the library how to find an item's parent so it can determine which subscription applies.

With these pieces registered, the library handles much of the underlying connection, access control, propagation, and persistence work. Your handlers provide the behaviour specific to your domain.

For now, Bonafide Technologies Inc. operates the infrastructure these apps run on. Independent hosting will become possible with router federation. See Hosting Bonafide Apps.

3. Check compatibility

If you're implementing an existing domain, your app needs to follow the same contract as other apps using it. That's what allows them to interoperate.

We're developing an automated conformance test suite to help developers check their implementations before launch.

Until that's available, compatibility is reviewed manually with us.

4. Go live

Once your implementation is ready, we'll work with you to check that it follows the relevant domain specification before enabling interoperability with other apps.

For now, this is a manual process. The plan is to replace much of it with automated conformance testing as the tooling develops.

Building an app in the Pools domain

Pools is built around private spaces where people share content with selected connections. Members can post, react, and have threaded conversations with their connections in that pool.

The domain defines how pools, posts, memberships, threads, messages, and reactions are represented. It also defines how content is owned, shared, and propagated. Threads inherit their membership from the posts they're attached to.

Another app implementing the bonafide.pools domain can work with that same content model and its rules. That's the point of publishing the domain: developers can build different applications without creating separate, incompatible networks for the people using them.

See the Pools domain vocabulary for the complete content model.

Building an app in the messaging domain

We're working on the bonafide.messaging domain, but its vocabulary hasn't been published yet.

If you're interested in building a messaging app on Bonafide, get in touch.

Adding new domains

Pools and messaging won't be the only domains, and we don't intend to be the only people defining them.

New domain proposals follow the same public change process as the base specification.

  • Propose a domain. Open a GitHub issue or submit a pull request adding a file under domains/ in the Bonafide Protocol repository.
  • Explain what it needs to do. Describe the problem, the content types and relationships involved, and why an existing domain doesn't already cover it.
  • Participate in the review. For now, Bonafide Technologies Inc. makes the final decisions. Proposals, discussion, and decisions are documented publicly, including the reasoning behind them.

We plan to move stewardship of this process to the Bonafide Foundation as it develops.