File: README.md
What is Spin?
Spin is an open source framework for building and running fast, secure, and
composable cloud microservices with WebAssembly. It aims to be the easiest way
to get started with WebAssembly microservices, and takes advantage of the latest
developments in the
WebAssembly component model
and Wasmtime runtime.
Spin offers a simple CLI that helps you create, distribute, and execute
applications, and in the next sections we will learn more about Spin
applications and how to get started.
Getting started
See the Install Spin page of the Spin documentation for a detailed
guide on installing and configuring Spin, but in short run the following commands:
curl -fsSL https://spinframework.dev/downloads/install.sh | bash
sudo mv ./spin /usr/local/bin/spinAlternatively, you could build Spin from source.
To get started writing apps, follow the quickstart guide,
and then follow the
Rust, JavaScript, Python, or Go
language guides, and the guide on writing Spin applications.
Language support
WebAssembly is a language-agnostic runtime: you can build WebAssembly components from a variety of source languages. Spin SDKs are available for several languages, including:
- JavaScript: https://github.com/spinframework/spin-js-sdk
- Rust: https://crates.io/crates/spin-sdk
- Go: https://pkg.go.dev/github.com/fermyon/spin/sdk/go/v2
- Python: https://github.com/spinframework/spin-python-sdk
- Zig: https://github.com/dasimmet/zig-spin (third party)
- Moonbit: https://github.com/gmlewis/spin-moonbit-sdk (third party)
The Spin framework team supports the JavaScript, Rust, Go, and Python SDKs. Other language integrations are supported by their authors, and we're grateful to them for their work!
Usage
Below is an example of using the spin CLI to create a new Spin application. To run the example you will need to install the wasm32-wasip2 target for Rust.
$ rustup target add wasm32-wasip2First, run the spin new command to create a Spin application from a template.
# Create a new Spin application named 'hello-rust' based on the Rust http template, accepting all defaults
$ spin new --accept-defaults -t http-rust hello-rustRunning the spin new command created a hello-rust directory with all the necessary files for your application. Change to the hello-rust directory and build the application with spin build, then run it locally with spin up:
# Compile to Wasm by executing the `build` command.
$ spin build
Executing the build command for component hello-rust: cargo build --target wasm32-wasip2 --release
Finished release [optimized] target(s) in 0.03s
Successfully ran the build command for the Spin components.
# Run the application locally.
$ spin up
Logging component stdio to ".spin/logs/"
Serving http://127.0.0.1:3000
Available Routes:
hello-rust: http://127.0.0.1:3000 (wildcard)That's it! Now that the application is running, use your browser or cURL in another shell to try it out:
# Send a request to the application.
$ curl -i 127.0.0.1:3000
HTTP/1.1 200 OK
content-type: text/plain
transfer-encoding: chunked
date: Sun, 02 Mar 2025 20:09:11 GMT
Hello World!You can make the app do more by editting the src/lib.rs file in the hello-rust directory using your favorite editor or IDE. To learn more about writing Spin applications see Writing Applications in the Spin documentation. To learn how to publish and distribute your application see the Publishing and Distribution guide in the Spin documentation.
Language Support for Spin Features
The table below summarizes the feature support in each of the language SDKs.
| Feature | Rust SDK Supported? | TypeScript SDK Supported? | Python SDK Supported? | Tiny Go SDK Supported? | C# SDK Supported? |
|---|---|---|---|---|---|
| Triggers | |||||
| HTTP | Supported | Supported | Supported | Supported | Supported |
| Redis | Supported | Supported | Supported | Supported | Not Supported |
| APIs | |||||
| Outbound HTTP | Supported | Supported | Supported | Supported | Supported |
| Configuration Variables | Supported | Supported | Supported | Supported | Supported |
| Key Value Storage | Supported | Supported | Supported | Supported | Not Supported |
| SQLite Storage | Supported | Supported | Supported | Supported | Not Supported |
| MySQL | Supported | Supported | Not Supported | Supported | Not Supported |
| PostgreSQL | Supported | Supported | Not Supported | Supported | Supported |
| Outbound Redis | Supported | Supported | Supported | Supported | Supported |
| Serverless AI | Supported | Supported | Supported | Supported | Not Supported |
| Extensibility | |||||
| Authoring Custom Triggers | Supported | Not Supported | Not Supported | Not Supported | Not Supported |
Getting Involved and Contributing
We are delighted that you are interested in making Spin better! Thank you!
Each Monday at 9:00pm UTC, we meet to discuss Spin issues, roadmap, and ideas in our Spin Project Meetings. Demos are welcome. Link to the meeting can be found in the Spin Project Meeting agenda below.
If this meeting time does not work for you, please propose a one-off meeting at a different time or a new recurring time in the Spin Slack Channel or in the issue queue. We will do our best to accommodate.
The Spin Project Meeting agenda is a public document. The document contains a rolling agenda with the date and time of each meeting, the Zoom link, and topics of discussion for the day. You will also find the meeting minutes for each meeting and the link to the recording. If you have something you would like to demo or discuss at the project meeting, we encourage you to add it to the agenda.
You can find the contributing guide here.
Stay in Touch
Follow us on Twitter: @spinframework
You can join the Spin community in the Spin CNCF Slack channel where you can ask questions, get help, and show off the cool things you are doing with Spin!
File: crates/test-codegen-macro/README.md
Test Codegen Macro
A macro for automatically producing #[test] annotated functions based on file directory structure. This is used by the runtime tests so that when adding a runtime test, you're not required to also add a test function corresponding to that runtime test.
File: crates/componentize/adapters/README.md
Adapters
The componentize process uses adapters to adapt plain wasm modules to wasi preview 2 compatible wasm components. There are three adapters that are built and stored as wasm binaries in this repository:
- The upstream wasi preview1 adapters for both commands and reactors for use with newer versions of wit-bindgen (v0.5 and above).
- These are currently the wasmtime 18.0.1 release.
- A modified adapter that has knowledge of Spin APIs for use with v0.2 of wit-bindgen which has a different ABI than newer wit-bindgen based modules.
File: crates/componentize/README.md
spin-componentize
This library converts a Spin module to a
component.
Note that although the world specifies both inbound-redis and inbound-http
exports, spin-componentize will only export either or both according to what
the original module exported.
Building
This crate requires a Rust installation v1.68 or later and a couple of Wasm targets:
rustup target add wasm32-wasip1
rustup target add wasm32-unknown-unknownNote that this is currently only a library and does not yet have a CLI interface, although that
would be easy to add if desired.
Testing
To test whether the spin componentize process produces wasm components that can be used with wasmtime, we run "abi conformance" testing. These tests are run with a plain cargo test invocation.
Wit and Adapters
spin-componentize and the abi conformance tests use component adapters built from wasmtime.
See the adapters README for more information.
File: cross/README.md
Polyfill for missing SIMD intrinsics in `cross-rs` image for target `aarch64-unknown-linux-musl`
A transitive dependency of spin (llama.cpp via ggml) uses the vld1q_s8_x4 and vld1q_u8_x4 compiler built-in SIMD intrinsics.
These intrinsics are missing for aarch64 in gcc < 10.3, while cross-rs ships with gcc 9 for aarch64-unknown-linux-musl as of writing.
The code in this folder does a feature check and patches the arm_neon.h header with polyfills if the functions are missing.
See https://github.com/spinframework/spin/issues/1786 for the upstream issue.
File: deploy/README.md
Deployments
The Spin Docs website is deployed via the deploy-website.yaml GitHub workflow.
(Note: currently this website consists of redirects to the Spin Docs hosted at spinframework.dev)
Auto Deploys
The production version of the website is deployed whenever commits are pushed to the main branch.
Manual Deploys
Deployments may also be triggered manually, providing a choice of ref and sha.
Nomad jobs
We currently deploy the website via its Nomad job directly. (In the future, we envision running the website as a Fermyon Cloud app.)
The spin-docs Nomad job contains configuration for the running website, including the OCI reference to run from.
File: docs/content/sips/001-spin-deploy.md
title = "SIP 001 - Spin Deploy" template = "main" date = "2022-03-18T14:53:30Z"
Summary: A Spin CLI command that will deploy a Spin application to the cloud
Owner: [email protected]
Created: March 18, 2022
Updated: March 31, 2022
Background
A Spin application โ (currently described as a spin.toml file) โ is a collection of one or more components, each invoked as a result of an event generated by a trigger. The Spin CLI currently enables running a Spin application locally via the spin up command. The Spin CLI also offers a set of commands enabling users to package and push a Spin application to a Bindle registry.
Hippo is a self-hosted, open source, Web Assembly PaaS that allows users to deploy and scale Web Assembly applications. Hippo takes a reference to a bindle, deploys it to the cloud and makes it publicly available. Hippo currently only works with Wagi applications and can currently deploy Wagi applications on a Nomad cluster with Traefik for routing. Additionally, Hippo has a concept called โchannelsโ enabling users to track different version categories of an application (ex. alpha, beta, stable, pre-releases, minor versions, etc.).
A desired feature is to be able to deploy a Spin application to the cloud using Hippo so that it is accessible via a publicly available domain or IP address.
Proposal
Create a spin deploy command. This command packages a Spin application as a bindle, pushes it to a bindle registry and instructs Hippo to deploy the application to the cloud.
Configuration
This command needs the following configuration to be passed via either flags or environment variables or config file (config file to be defined at a later date) to deploy the application properly and to the right place:
- Bindle server information
- Environment variable: BINDLE_SERVER_URL
- Flag: --bindle-server
- Hippo URL information
- Environment variable: HIPPO_URL
- Flag: --hippo-url
Authentication with Hippo
Hippo authentication information. A token is needed to communicate with the Hippo server. This can be
retrieved either from requesting it from the Hippo server with a username and password, from the Hippo config file on the system (if a user has already logged in) or from the user if they already know the
token information. The following flags and environment variables support authenticating with Hippo.
- Hippo token
- Environment variable: HIPPO_TOKEN
- Flag: --hippo-token
- Hippo username
- Environment variable: HIPPO_USERNAME
- Flag: --hippo-username
- Hippo password
- Environment variable: HIPPO_PASSWORD
- Flag: --hippo-password
The --hippo-token flag cannot be set in combination with the --hippo-username and --hippo-password flags. If so, Spin will return an error. If the username/password flags are set, Spin will request a token from the Hippo server and use it. If the username flag was provided but the password was not, Spin will prompt the user for a password. If none of the Hippo auth flags are provided, Spin will find the Hippo token in the Hippo config file on disk. If Hippo cannot find the file or the token in the file is expired, Spin will prompt the user for username/password.
Flow
As part of the deploy flow, Spin will:
- Create and push a bindle for the Spin app
- Create a Hippo app with the name given in the Spin.toml file. If app exists, it will use the existing app's ID for the next step. Note: Should we even be re-using an app? or should we create a unique app per deploy? I'm leaning towards the latter?
- Create a Hippo channel called "spin-deploy" (might be something we should allow for configuration via a flag). If the channel already exists, ensure that revision information is configured to pick up the new bindle version.
- Register a revision with Hippo so that the relevant channel(s) deploy the application.
Pre-requsites
In order to use the spin deploy command, a user must already have Hippo installed and running using the nomad scheduler and a bindle url.
Bindle versioning
A bindle is immutable once pushed and the tag cannot be overwritten. Therefore, we'll need a way to construct unique tags. One way to do this is to hash the bindle invoice that is generated.
Note: I believe Hippo only allows semver versions though and in that case, this would not work or we'd need to see if we can make Hippo pick up non-semver bindle tags.
Assumptions about the Hippo Platform
The following is a list of assumptions we are making about the Hippo platform that we would need to build in order for this command to work.
- There needs to be a single endpoint on the Hippo API Server that takes the Spin application information and creates a Hippo application with a default channel configured to watch the correct bindle reference. See hippo-cli issue #97.
- Hippo needs to support running Spin applications and not only Wagi applications. See hippo issue #579.
Additional Consideration: Plugin Model
In the future, we should make this command a plugin for the Spin CLI. People may not want to use the Spin deploy command to deploy to Hippo. They may want to deploy to a different target using a different platform. They should be able to do that via plugins. However, there is no plugin system in Spin at this time and that may require further debate and discussion and consideration, so it's best to continue with spin deploy as a built-in subcommand for the time being to be focused about the scope of work.
File: docs/content/sips/002-app-config.md
title = "SIP 002 - Application Configuration" template = "main" date = "2022-03-22T14:53:30Z"
Summary: A configuration system for Spin applications.
Owner: [email protected]
Created: March 22, 2022
Updated: July 19, 2022
Background
It is common for applications to require configuration at runtime that isn't known at build time or is too sensitive to be stored with build artifacts:
- Logging configuration
- Per-channel (production, staging, etc) service dependency URLs
- Database secrets
Proposal
Configuration schema is defined by components and applications
Configuration within a "parent" (component or application) consists of a number of configuration "slots":
- Slots are identified by a string "key"; in order to allow unambiguous conversion to environment variables or file paths, keys are constrained:
- Keys must start with a letter (required for env vars)
- Keys consist of only lowercase ascii alphanum and
_([a-z0-9_])- Only one
_at a time and not at the end (to allow delimiting in env vars with__)
- Only one
- Slot keys must be unique within their parent, but to allow independent development of components different parents may have identical keys
- A slot must either be marked as "required" or must be given a default value
- A slot may be marked as "secret", in which case any associated value should be handled with care (e.g. not logged)
[variables]
required_key = { required = true }
optional_key = { default = "default_value" }
secret_key = { required = true, secret = true }Default values can use template strings to reference other slots.
[variables]
key1 = { required = true }
key2 = { default = "prefix-{{ key1 }}-suffix" }Components and applications set configuration values of their dependencies
In dependency configuration, templates strings can reference top-level config keys (those in [variables]), "sibling" keys within the same dependency, and "ancestor" dependant configs.
- Top-level references use just the key name:
{{ top_level_key }} - "Sibling" references use a single
.prefix:{{ .sibling_key }} - "Ancestor" references use multiple
.s:{{ ..parent_dep_key }},{{ ...grandparent_key }} - Circular / infinitely recursive references are not permitted
spin.toml:
[variables]
app_root = { default = "/app" }
log_file = { default = "{{ app_root }}/log.txt" }
...
[[component.config]]
work_root = "{{ app_root }}/work" # -> "/app/work"
work_out = "{{ .work_root }}/output" # -> "/app/work/output"
[[component.dependencies.dep1.config]]
dep_root = "{{ ..work_root }}/dep" # -> "/app/work/dep"Configuration "providers" resolve application configuration
When resolving the value of an application configuration slot, providers will be queried in-order for a value. If no value is returned by any provider, the resolution will either use the default value or fail (if the slot is "required").
Provider configuration is handled by spin at instantiation time (spin up).
Note: Provider configuration is TBD; as TOML it could look like:
[[config-provider]]
type = "json_file"
path = "config.json"
[[config-provider]]
type = "env"
prefix = "MY_APP_"Example providers
- Environment provider
- Configured with a prefix, e.g.
SPIN_CONFIG_ key_one->SPIN_CONFIG_KEY_ONE
- Configured with a prefix, e.g.
- File providerjson
{"key_one": "value-one"} - Vault provider
Configuration is exposed via component interface
spin-config.wit
// Unknown key is a runtime error
get-config: function(key: string) -> expect<string>- Since each component gets its own instance of the
spin-configimport, the executor can resolve keys automatically and only expose a component's own config to it.
Future design options
This section contains possible future features which are not fully defined here.
Typed configuration
The above assumes only string values, but we could include some typing:
# Type can be inferred from default value:
number_key = { default = 123 }
# equivalent to:
number_key = { type = "number", default = 123 }
required_string = { type = "string", required = true }
# "bytes" would require e.g. base64 encoding in some places
encryption_key = { type = "bytes", required = true, secret = true}WASI "configfs"
For languages without component support, we could expose config as synthetic mounted files:
key1_value = File.read("/config/key1")
# Typed config; `.json` encodes values to JSON
key1_value = JSON.parse(File.read("/config/key1.json"))
# "bytes" type; `.raw` decodes from base64
encryption_key = File.read("/config/encryption_key.raw")File: docs/content/sips/003-trigger-executors.md
title = "SIP 003 - Trigger Executors" template = "main" date = "2022-04-29T14:53:30Z"
Summary: A new architecture for Spin trigger executors.
Owner: [email protected]
Created: April 29, 2022
Updated: May 5, 2022
Background
Spin currently includes built-in implementations for two trigger types: HTTP, and Redis (PubSub).
We want to allow for additional trigger types in the future, whether they are "internal" types developed and
supported within the Spin codebase, or "external" types developed by the broader community.
While it is currently possible to build external trigger types - as in the
TimerTrigger example - it requires replicating much of the
functionality provided by the spin up command.
Proposal
Trigger types are implemented as "trigger executors"
These executors are regular subcommands/binaries, but are not normally executed directly by
users.
- "Internal" triggers use
spinsubcommands, e.g.spin trigger http - "External" triggers use external binaries, e.g.
spin-trigger-timer - Trigger executors share common code, including common CLI options in a
spin-triggercrate
The `spin up` command changes
It remains the primary entrypoint for starting a Spin app, but its behavior is changed to:
- Load the application manifest
- Find a trigger executor for the application (e.g. check the
PATHfor"spin-trigger-${trigger.type}") - Set some environment variables:
SPIN_MANIFEST_URL: a URL pointing to the application manifest (e.g.file:///path/to/spin.tomlorbindle+https://bindle-server?id=bindle-id)SPIN_TRIGGER_TYPEthe spin trigger type (e.g.http) being executed
execthe trigger executor, forwarding its own CLI arguments
Future design options
This section contains possible future features which are not fully defined here.
Multi-trigger orchestration
If multiple trigger types were to be supported by a single application, the role of spin up could
grow to include trigger orchestration:
- Finding executors for all required trigger types
- Forking a child process for each trigger executor
- Managing the child processes (restarts, graceful shutdowns, etc.)
Potential disadvantages
If the "Multi-trigger orchestration" option is taken, having separate processes for different triggers
might have some disadvantages:
- Multiple triggers types would use more system resources implemented as multiple processes rather than as
multiple async tasks in the same process, especially if a single module implements multiple trigger type
handlers. - There has been discussion the possibility of trigger handlers making optimized "in-memory" calls to
other triggers. This design would make cross-trigger-type calls of this sort more complex at best.