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:
- An Extension Point.
- The
xtp
CLI.
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.
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.
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.
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".