{"id":13774192,"url":"https://github.com/extism/go-pdk","last_synced_at":"2025-04-06T03:07:03.934Z","repository":{"id":60024408,"uuid":"529363025","full_name":"extism/go-pdk","owner":"extism","description":"Extism Plug-in Development Kit (PDK) for Go","archived":false,"fork":false,"pushed_at":"2025-03-18T02:40:40.000Z","size":850,"stargazers_count":69,"open_issues_count":3,"forks_count":13,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-30T02:04:39.818Z","etag":null,"topics":["extism","golang","pdk","plugin","wasm"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/extism/go-pdk","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/extism.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-26T18:17:19.000Z","updated_at":"2025-03-22T04:09:35.000Z","dependencies_parsed_at":"2023-10-20T01:24:06.190Z","dependency_job_id":"ab36f3cb-27dc-4694-8d49-3aa6b7392186","html_url":"https://github.com/extism/go-pdk","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fgo-pdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fgo-pdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fgo-pdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extism%2Fgo-pdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/extism","download_url":"https://codeload.github.com/extism/go-pdk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247427006,"owners_count":20937201,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["extism","golang","pdk","plugin","wasm"],"created_at":"2024-08-03T17:01:24.534Z","updated_at":"2025-04-06T03:07:03.913Z","avatar_url":"https://github.com/extism.png","language":"Go","funding_links":[],"categories":["Go","\u003ca name=\"extism\"\u003e\u003c/a\u003e[Extism](https://github.com/extism/extism) \u003csup\u003e[top⇈](#contents)\u003c/sup\u003e"],"sub_categories":[],"readme":"# Extism Go PDK\n\nThis library can be used to write\n[Extism Plug-ins](https://extism.org/docs/concepts/plug-in) in Go.\n\n## Install\n\nInclude the library with Go get:\n\n```bash\ngo get github.com/extism/go-pdk\n```\n\n## Reference Documentation\n\nYou can find the reference documentation for this library on\n[pkg.go.dev](https://pkg.go.dev/github.com/extism/go-pdk).\n\n## Getting Started\n\nThe goal of writing an\n[Extism plug-in](https://extism.org/docs/concepts/plug-in) is to compile your Go\ncode to a Wasm module with exported functions that the host application can\ninvoke. The first thing you should understand is creating an export. Let's write\na simple program that exports a `greet` function which will take a name as a\nstring and return a greeting string. Paste this into your `main.go`:\n\n```go\npackage main\n\nimport (\n\t\"github.com/extism/go-pdk\"\n)\n\n//go:wasmexport greet\nfunc greet() int32 {\n\tinput := pdk.Input()\n\tgreeting := `Hello, ` + string(input) + `!`\n\tpdk.OutputString(greeting)\n\treturn 0\n}\n```\n\nSome things to note about this code:\n\n1. The `//go:wasmexport greet` comment is required. This marks the greet function as an\n   export with the name `greet` that can be called by the host.\n2. Exports in the Go PDK are coded to the raw ABI. You get parameters from the\n   host by calling\n   [pdk.Input* functions](https://pkg.go.dev/github.com/extism/go-pdk#Input) and\n   you send returns back with the\n   [pdk.Output* functions](https://pkg.go.dev/github.com/extism/go-pdk#Output).\n3. An Extism export expects an i32 return code. `0` is success and `1` is a\n   failure.\n\nInstall the `tinygo` compiler:\n\nSee https://tinygo.org/getting-started/install/ for instructions for your\nplatform.\n\n\u003e Note: while the core Go toolchain has support to target WebAssembly, we find\n\u003e `tinygo` to work well for plug-in code. Please open issues on this repository\n\u003e if you try building with `go build` instead \u0026 have problems!\n\nCompile this with the command:\n\n```bash\ntinygo build -o plugin.wasm -target wasip1 -buildmode=c-shared main.go\n```\n\nWe can now test `plugin.wasm` using the\n[Extism CLI](https://github.com/extism/cli)'s `run` command:\n\n```bash\nextism call plugin.wasm greet --input \"Benjamin\" --wasi\n# =\u003e Hello, Benjamin!\n```\n\n\u003e **Note**: Currently `wasip1` must be provided for all Go plug-ins even if they\n\u003e don't need system access, however this will eventually be optional.\n\n\u003e **Note**: We also have a web-based, plug-in tester called the\n\u003e [Extism Playground](https://playground.extism.org/)\n\n### More Exports: Error Handling\n\nSuppose we want to re-write our greeting module to never greet Benjamins. We can\nuse [pdk.SetError](https://pkg.go.dev/github.com/extism/go-pdk#SetError) or\n[pdk.SetErrorString](https://pkg.go.dev/github.com/extism/go-pdk#SetErrorString):\n\n```go\n//go:wasmexport greet\nfunc greet() int32 {\n\tname := string(pdk.Input())\n\tif name == \"Benjamin\" {\n\t\tpdk.SetError(errors.New(\"Sorry, we don't greet Benjamins!\"))\n\t\treturn 1\n\t}\n\tgreeting := `Hello, ` + name + `!`\n\tpdk.OutputString(greeting)\n\treturn 0\n}\n```\n\nNow when we try again:\n\n```bash\nextism call plugin.wasm greet --input=\"Benjamin\" --wasi\n# =\u003e Error: Sorry, we don't greet Benjamins!\n# =\u003e returned non-zero exit code: 1\necho $? # print last status code\n# =\u003e 1\nextism call plugin.wasm greet --input=\"Zach\" --wasi\n# =\u003e Hello, Zach!\necho $?\n# =\u003e 0\n```\n\n### Json\n\nExtism export functions simply take bytes in and bytes out. Those can be\nwhatever you want them to be. A common and simple way to get more complex types\nto and from the host is with json:\n\n```go\ntype Add struct {\n\tA int `json:\"a\"`\n\tB int `json:\"b\"`\n}\n\ntype Sum struct {\n\tSum int `json:\"sum\"`\n}\n\n//go:wasmexport add\nfunc add() int32 {\n\tparams := Add{}\n\t// use json input helper, which automatically unmarshals the plugin input into your struct\n\terr := pdk.InputJSON(\u0026params)\n\tif err != nil {\n\t\tpdk.SetError(err)\n\t\treturn 1\n\t}\n\tsum := Sum{Sum: params.A + params.B}\n\t// use json output helper, which automatically marshals your struct to the plugin output\n\t_, err := pdk.OutputJSON(sum)\n\tif err != nil {\n\t\tpdk.SetError(err)\n\t\treturn 1\n\t}\n\treturn 0\n}\n```\n\n```bash\nextism call plugin.wasm add --input='{\"a\": 20, \"b\": 21}' --wasi\n# =\u003e {\"sum\":41}\n```\n\n## Configs\n\nConfigs are key-value pairs that can be passed in by the host when creating a\nplug-in. These can be useful to statically configure the plug-in with some data\nthat exists across every function call. Here is a trivial example using\n[pdk.GetConfig](https://pkg.go.dev/github.com/extism/go-pdk#GetConfig):\n\n```go\n//go:wasmexport greet\nfunc greet() int32 {\n\tuser, ok := pdk.GetConfig(\"user\")\n\tif !ok {\n\t\tpdk.SetErrorString(\"This plug-in requires a 'user' key in the config\")\n\t\treturn 1\n\t}\n\tgreeting := `Hello, ` + user + `!`\n\tpdk.OutputString(greeting)\n\treturn 0\n}\n```\n\nTo test it, the [Extism CLI](https://github.com/extism/cli) has a `--config`\noption that lets you pass in `key=value` pairs:\n\n```bash\nextism call plugin.wasm greet --config user=Benjamin\n# =\u003e Hello, Benjamin!\n```\n\n## Variables\n\nVariables are another key-value mechanism but it's a mutable data store that\nwill persist across function calls. These variables will persist as long as the\nhost has loaded and not freed the plug-in.\n\n```go\n//go:wasmexport count\nfunc count() int32 {\n\tcount := pdk.GetVarInt(\"count\")\n\tcount = count + 1\n\tpdk.SetVarInt(\"count\", count)\n\tpdk.OutputString(strconv.Itoa(count))\n\treturn 0\n}\n```\n\n\u003e **Note**: Use the untyped variants\n\u003e [pdk.SetVar(string, []byte)](https://pkg.go.dev/github.com/extism/go-pdk#SetVar)\n\u003e and\n\u003e [pdk.GetVar(string) []byte](https://pkg.go.dev/github.com/extism/go-pdk#GetVar)\n\u003e to handle your own types.\n\n## Logging\n\nBecause Wasm modules by default do not have access to the system, printing to\nstdout won't work (unless you use WASI). Extism provides a simple\n[logging function](https://pkg.go.dev/github.com/extism/go-pdk#Log) that allows\nyou to use the host application to log without having to give the plug-in\npermission to make syscalls.\n\n```go\n//go:wasmexport log_stuff\nfunc logStuff() int32 {\n\tpdk.Log(pdk.LogInfo, \"An info log!\")\n\tpdk.Log(pdk.LogDebug, \"A debug log!\")\n\tpdk.Log(pdk.LogWarn, \"A warn log!\")\n\tpdk.Log(pdk.LogError, \"An error log!\")\n\treturn 0\n}\n```\n\nFrom [Extism CLI](https://github.com/extism/cli):\n\n```bash\nextism call plugin.wasm log_stuff --wasi --log-level=debug\n2023/10/12 12:11:23 Calling function : log_stuff\n2023/10/12 12:11:23 An info log!\n2023/10/12 12:11:23 A debug log!\n2023/10/12 12:11:23 A warn log!\n2023/10/12 12:11:23 An error log!\n```\n\n\u003e _Note_: From the CLI you need to pass a level with `--log-level`. If you are\n\u003e running the plug-in in your own host using one of our SDKs, you need to make\n\u003e sure that you call `set_log_file` to `\"stdout\"` or some file location.\n\n## HTTP\n\nSometimes it is useful to let a plug-in\n[make HTTP calls](https://pkg.go.dev/github.com/extism/go-pdk#HTTPRequest.Send).\n[See this example](example/http/tiny_main.go)\n\n```go\n//go:wasmexport http_get\nfunc httpGet() int32 {\n\t// create an HTTP Request (withuot relying on WASI), set headers as needed\n\treq := pdk.NewHTTPRequest(pdk.MethodGet, \"https://jsonplaceholder.typicode.com/todos/1\")\n\treq.SetHeader(\"some-name\", \"some-value\")\n\treq.SetHeader(\"another\", \"again\")\n\t// send the request, get response back (can check status on response via res.Status())\n\tres := req.Send()\n\n\tpdk.OutputMemory(res.Memory())\n\n\treturn 0\n}\n```\n\nBy default, Extism modules cannot make HTTP requests unless you specify which\nhosts it can connect to. You can use `--alow-host` in the Extism CLI to set\nthis:\n\n```\nextism call plugin.wasm http_get --wasi --allow-host='*.typicode.com'\n# =\u003e { \"userId\": 1, \"id\": 1, \"title\": \"delectus aut autem\", \"completed\": false }\n```\n\n## Imports (Host Functions)\n\nLike any other code module, Wasm not only let's you export functions to the\noutside world, you can import them too. Host Functions allow a plug-in to import\nfunctions defined in the host. For example, if you host application is written\nin Python, it can pass a Python function down to your Go plug-in where you can\ninvoke it.\n\nThis topic can get fairly complicated and we have not yet fully abstracted the\nWasm knowledge you need to do this correctly. So we recommend reading our\n[concept doc on Host Functions](https://extism.org/docs/concepts/host-functions)\nbefore you get started.\n\n### A Simple Example\n\nHost functions have a similar interface as exports. You just need to declare\nthem as extern on the top of your main.go. You only declare the interface as it\nis the host's responsibility to provide the implementation:\n\n```go\n//go:wasmimport extism:host/user a_python_func\nfunc aPythonFunc(uint64) uint64\n```\n\nWe should be able to call this function as a normal Go function. Note that we\nneed to manually handle the pointer casting:\n\n```go\n//go:wasmexport hello_from_python\nfunc helloFromPython() int32 {\n    msg := \"An argument to send to Python\"\n    mem := pdk.AllocateString(msg)\n    defer mem.Free()\n    ptr := aPythonFunc(mem.Offset())\n    rmem := pdk.FindMemory(ptr)\n    response := string(rmem.ReadBytes())\n    pdk.OutputString(response)\n    return 0\n}\n```\n\n### Testing it out\n\nWe can't really test this from the Extism CLI as something must provide the\nimplementation. So let's write out the Python side here. Check out the\n[docs for Host SDKs](https://extism.org/docs/concepts/host-sdk) to implement a\nhost function in a language of your choice.\n\n```python\nfrom extism import host_fn, Plugin\n\n@host_fn()\ndef a_python_func(input: str) -\u003e str:\n    # just printing this out to prove we're in Python land\n    print(\"Hello from Python!\")\n\n    # let's just add \"!\" to the input string\n    # but you could imagine here we could add some\n    # applicaiton code like query or manipulate the database\n    # or our application APIs\n    return input + \"!\"\n```\n\nNow when we load the plug-in we pass the host function:\n\n```python\nmanifest = {\"wasm\": [{\"path\": \"/path/to/plugin.wasm\"}]}\nplugin = Plugin(manifest, functions=[a_python_func], wasi=True)\nresult = plugin.call('hello_from_python', b'').decode('utf-8')\nprint(result)\n```\n\n```bash\npython3 app.py\n# =\u003e Hello from Python!\n# =\u003e An argument to send to Python!\n```\n\n## Reactor modules\n\nSince TinyGo version 0.34.0, the compiler has native support for \n[Reactor modules](https://dylibso.com/blog/wasi-command-reactor/).\n\nMake sure you invoke the compiler with the `-buildmode=c-shared` flag\nso that libc and the Go runtime are properly initialized:\n\n```bash\ncd example/reactor\ntinygo build -target wasip1 -buildmode=c-shared -o reactor.wasm ./tiny_main.go\nextism call ./reactor.wasm read_file --input \"./test.txt\" --allow-path . --wasi --log-level info\n# =\u003e Hello World!\n```\n\n### Note on TinyGo 0.33.0 and earlier\n\nTinyGo versions below 0.34.0 do not support\n[Reactor modules](https://dylibso.com/blog/wasi-command-reactor/).\nIf you want to use WASI inside your Reactor module functions (exported functions other\nthan `main`). You can however import the `wasi-reactor` module to ensure that libc\nand go runtime are initialized as expected:\n\nMoreover, older versions may not provide the special `//go:wasmexport` \ndirective, and instead use `//export`.\n\n```go\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/extism/go-pdk\"\n\t_ \"github.com/extism/go-pdk/wasi-reactor\"\n)\n\n//export read_file\nfunc read_file() {\n\tname := pdk.InputString()\n\n\tcontent, err := os.ReadFile(name)\n\tif err != nil {\n\t\tpdk.Log(pdk.LogError, err.Error())\n\t\treturn\n\t}\n\n\tpdk.Output(content)\n}\n\nfunc main() {}\n```\n\n```bash\ntinygo build -target wasip1 -o reactor.wasm ./tiny_main.go\nextism call ./reactor.wasm read_file --input \"./test.txt\" --allow-path . --wasi --log-level info\n# =\u003e Hello World!\n```\n\nNote: this is not required if you only have the `main` function.\n\n## Generating Bindings\n\nIt's often very useful to define a schema to describe the function signatures\nand types you want to use between Extism SDK and PDK languages.\n\n[XTP Bindgen](https://github.com/dylibso/xtp-bindgen) is an open source\nframework to generate PDK bindings for Extism plug-ins. It's used by the\n[XTP Platform](https://www.getxtp.com/), but can be used outside of the platform\nto define any Extism compatible plug-in system.\n\n### 1. Install the `xtp` CLI.\n\nSee installation instructions\n[here](https://docs.xtp.dylibso.com/docs/cli#installation).\n\n### 2. Create a schema using our OpenAPI-inspired IDL:\n\n```yaml\nversion: v1-draft\nexports: \n  CountVowels:\n      input: \n          type: string\n          contentType: text/plain; charset=utf-8\n      output:\n          $ref: \"#/components/schemas/VowelReport\"\n          contentType: application/json\n# components.schemas defined in example-schema.yaml...\n```\n\n\u003e See an example in [example-schema.yaml](./example-schema.yaml), or a full\n\u003e \"kitchen sink\" example on\n\u003e [the docs page](https://docs.xtp.dylibso.com/docs/concepts/xtp-schema/).\n\n### 3. Generate bindings to use from your plugins:\n\n```\nxtp plugin init --schema-file ./example-schema.yaml\n    1. TypeScript                      \n  \u003e 2. Go                              \n    3. Rust                            \n    4. Python                          \n    5. C#                              \n    6. Zig                             \n    7. C++                             \n    8. GitHub Template                 \n    9. Local Template\n```\n\nThis will create an entire boilerplate plugin project for you to get started\nwith:\n\n```go\npackage main\n\n// returns VowelReport (The result of counting vowels on the Vowels input.)\nfunc CountVowels(input string) (VowelReport, error) {\n\t// TODO: fill out your implementation here\n\tpanic(\"Function not implemented.\")\n}\n```\n\nImplement the empty function(s), and run `xtp plugin build` to compile your\nplugin.\n\n\u003e For more information about XTP Bindgen, see the\n\u003e [dylibso/xtp-bindgen](https://github.com/dylibso/xtp-bindgen) repository and\n\u003e the official\n\u003e [XTP Schema documentation](https://docs.xtp.dylibso.com/docs/concepts/xtp-schema).\n\n## Reach Out!\n\nHave a question or just want to drop in and say hi?\n[Hop on the Discord](https://extism.org/discord)!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextism%2Fgo-pdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fextism%2Fgo-pdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextism%2Fgo-pdk/lists"}