Skip to main content

Integrate XTP

By now, you've created an application and at least one extension point. Now let's use them to fetch and execute plugins!

You'll need:

You have the option of integrating directly with the XTP REST API or using a SDK. This guide will cover the REST integration, but the concepts map directly to the SDK!

In this doc, we'll cover the process of developing your extension point. We start by adding ourselves as a Guest of the app in order to write plugins against our extension point. This will let us get a feel for changing the interface definition in the extension point schema. Then we'll cover fetching that plugin from the API.

Fetching a plugin for execution involves two HTTP calls. The first call lists available plugins for a guest along with information about where to fetch those plugins. The second call retrieves the plugin data itself, suitable for running using Extism.

note

For a more hands-on walkthrough of integrating XTP with an existing app, check out the XTP integration exercise repo.

Invite yourself as a guest

First, you'll need to add yourself as a guest of your application. This lets you get started publishing plugins to your application. This is handy to get a feel for what your guests will see!

Start by making sure that you're logged into the xtp CLI. Running the command below will open a browser window asking you to login.

$ xtp auth login

Once you've approved the login, close the browser window. Now we can add ourselves as a guest. We're going to use "my-guest-key" as our guest key for now.

note

Guest keys are controlled by your application; they're used to identify a plugin developer in XTP. These keys are private to your application: plugin developers cannot see them.

# First, let's list our apps
$ xtp app list

My apps

1. My great app (app_01j6ftcefcfshsna0wyny1k9y1)

# Then we use the app id to invite ourselves. We're using "my-guest-key" as our guest key.
$ curl -sL \
-H "authorization: Bearer $(xtp auth token show)" \
-H "content-type: application/json" \
-d '{"deliveryMethod":"link","guestKey": "my-guest-key"}' \
-X POST \
https://xtp.dylibso.com/api/v1/apps/app_01j6ftcefcfshsna0wyny1k9y1/guests

{"status":"ok","link":"https://xtp.dylibso.com/accept-invite?code=AZIMfs6fcnaS-j5KLGI177iMYrPpvc3PHWUR_RbHajqoSNLJZ9Fslg"}

Open that link in a browser to accept the guest invitation.

Write and publish a plugin

Let's write a basic plugin. The following command will ask a series of questions. Choose the extension point you just created, and pick TypeScript as the plugin.

$ xtp plugin init --name example --path example
$ cd example
$ xtp plugin push

This will push a stub plugin to your application, available under your user's guestKey.

Fetching available plugins

Use the extension binding route to fetch the plugin you just published:

$ curl -s -H "authorization: Bearer $(xtp auth token show)" \
https://xtp.dylibso.com/api/v1/extension-points/ext_4zdsqwhbev859a20p4791g42em/bindings/my-guest-key
{
"example": {
"id": "ext_4zdsqwhbev859a20p4791g42em/plugin/usr_6kk5gvfmvw83g924yc2gxzctzd/example",
"contentAddress": "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU",
"updatedAt": "2024-07-06T21:28:07.803Z"
}
}

In this case, we have a single plugin, example. Use the .contentAddress property to retrieve the plugin contents from the plugin content route:

$ curl -s -H "authorization: Bearer $(xtp auth token show)" \
-o plugin.wasm \
https://xtp.dylibso.com/api/v1/c/47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU

$ wasm-tools print plugin.wasm
(module
;; ...

This plugin is ready to be passed to the Extism Host SDK of your choice. Host SDKs are available for Rust, C, PHP, Python, JavaScript, and many more languages.

note

Because the content endpoint requires authentication, you'll need to fetch the plugin content before passing it to Extism.

// main.mjs
import { createPlugin } from '@extism/extism'

const plugin = await createPlugin('./plugin.wasm', { useWasi: true, runInWorker: true })
const result = await plugin.call('hello')
console.log(result.text())
await plugin.close()

Both of these endpoints are designed to be cache-friendly. The extension point binding endpoint respects if-none-match to reduce request size, reducing network bandwidth. The plugin content address endpoint is entirely immutable; it can be cached forever. The XTP SDKs perform this caching for you. They're a great option if you want to get started quickly!


Using the JS SDK

Alternatively, if your host is running Deno or Node.js, you can use the JS SDK:

import { createClient } from '@dylibso/xtp'

const xtpClient = await createClient({
token: String(process.env.XTP_TOKEN),
appId: String(process.env.XTP_APP_ID),
runInWorker: true,
logger: console,
})

const result = await xtp.extensionPoints.myExtensionPoint.myExport('myGreatGuestKey', {
'greeting': 'hello world'
}, { bindingName: 'bananadog'}, default: {})


Next Steps

We've covered the process of inviting guests to an application, publishing a plugin, and using the guest key to fetch plugins. If you have additional behavioral requirements for guest plugins, you can enforce that using simulations.

For more on how to invite guests outside of your organization to develop plugins, see "Invite Guests".