Components

The elements of a distributed app

The Webnative SDK is built on a few foundational blocks, the Webnative File System (WNFS), IPLD, CIDs, and UCANs. How these pieces are bound to an identity, communicated to other devices and users, encrypted, and transferred, is customizable through components. By default, Webnative uses the Fission infrastructure, but you don't have to.

There are seven components:

  • Capabilities. Determines how capabilities are requested and collected.

  • Crypto. How should private-public key pairs and symmetric keys be stored? How do I sign a message? Which key types should I support for the did:key method? What are my agent and sharing key pairs?

  • Depot. Gets IPLD data in and out of your program by creating and referencing CIDs. File system data is brought in and shipped out through this component.

  • Manners. Various behavioral elements. If the debug configuration flag is enabled, how does the logging happen, file system hooks, etc.

  • Reference. Responsible for the sources of truth: the data root (root CID of WNFS), the DID root (your account DID, aka. root agent DID), DNS, and repositories of various items.

  • Storage. Stores and retrieves ephemeral and session data.

Customization Examples

Walletauth

The webnative-walletauth plugin allows you to use your blockchain wallet to authenticate with webnative. It does not have an auth component because the identity already exists. But it does have crypto and manners components that make the following customizations:

  • The crypto component configures Webnative to work with the key pair used by an Ethereum wallet.

  • The manners component alters how Webnative transfers the root file system encryption key. Using file system hooks, the key is encrypted and put on the public side of the file system. The wallet can always decrypt this key, enabling file system access no matter what device connects.

Manners

In this example, we'll add a file system hook and emojis to the debug messages.

// example/manners/implementation.ts
import { Manners } from "webnative/components"
import { addSampleData } from "webnative/fs/data"

export async function implementation(
  options: Manners.ImplementationOptions
): Manners.Implementation {
  const base = WebnativeManners.implementation(options)
  const dontDoAnything = () => {}
  
  const hooks = {
    // Add some sample data to new file systems
    afterLoadNew: async (fs: FileSystem.API) => {
      addSampleData(fs)
      fs.publish()
    },
  }

  return {
    ...base,
    
    // Debug messages
    log: opts.configuration.debug ? msg => console.log("ℹ️", msg) : dontDoAnything,
    warn: opts.configuration.debug ? msg => console.warn("⚠️", msg) : dontDoAnything,

    // File system hooks
    fileSystem: { hooks }
  }
}

Using Components

To use a different component than Webnative's defaults, all you have to do is pass them into the program. Alternatively, if you have a whole composition (see the section below), you can skip a step and directly use the assemble function. That's what the program function uses underneath.

// Using our custom manners component implementation from earlier
import * as CustomManners from "./example/manners/implementation.ts"
import * as wn from "webnative"

const configuration = { namespace: "example" }
const program = await wn.program({
  ...configuration,
  manners: CustomManners.implementation({ configuration })
})

Compositions

A composition is a full set of components.

// The default set of components is available as:
wn.compositions.fission()

// To illustrate how the fission composition works:
const crypto = await wn.defaultCryptoComponent(configuration)
const manners = wn.defaultMannersComponent(configuration)
const storage = wn.defaultStorageComponent(configuration)

const configWithComponents = { ...configuration, crypto, manners, storage }

const r = await wn.reference.fission(configWithComponents)
const d = await wn.depot.fissionIPFS(configWithComponents)
const c = await wn.capabilities.fissionLobby({ ...configWithComponents, depot: d })
const a = await wn.auth.fissionWebCrypto({ ...configWithComponents, reference: r })

const composition = {
  auth: a,
  capabilities: c,
  depot: d,
  reference: r,
  crypto,
  manners,
  storage,
}

const program = await wn.assemble(
  configuration,
  composition
)

Last updated