diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a6dcd8bf9..255130556 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -8,7 +8,6 @@ This documentation has been checked to ensure that: - [ ] Does this PR have a new menu item (anywhere in `templates/*.hbs` files) that points to a document `.md` that is set to publish in the future? If so please only publish the `.md` and `.hbs` changes in real-time (otherwise there will be a menu item pointing to a `.md` file that does not exist) - [ ] File does not use CRLF, but uses plain LF (hint: use `cat -ve | grep '^M' | wc -l` and expect 0 as a result) - [ ] Has passed [`bart check`](https://developer.fermyon.com/bartholomew/quickstart) -- [ ] Has run `npm run build-index` - [ ] Has been manually tested by running in Spin/Bartholomew (hint: use `PREVIEW_MODE=1` and run `npm run styles` to update styling) - [ ] Headings are using Title Case - [ ] Code blocks have the programming language set to properly highlight syntax and the proper copy directive diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index 84671f37a..0f960f000 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -17,6 +17,16 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} + - name: Install npm packages + run: | + npm ci + + # TODO: any other node-y things to do prior to build/deploy? 'npm run styles', etc.? + + - name: Create search index + run: | + npm run build-index + - name: build and deploy preview uses: fermyon/actions/spin/preview@v1 with: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index de9e18a35..71e28d6a0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,6 +4,7 @@ on: push: branches: - 'main' + - 'vdice/oci-deploy' # TODO: remove after testing workflow_dispatch: inputs: @@ -29,7 +30,8 @@ permissions: id-token: write # Allow the workflow to create a JWT for AWS auth env: - JOB: fermyon-developer + OCI_REGISTRY: docker.io + OCI_IMAGE: fermyon/developer jobs: echo-inputs: @@ -38,12 +40,73 @@ jobs: steps: - name: Echo Inputs run: | - echo ref: ${{ github.event.inputs.ref }} - echo commit: ${{ github.event.inputs.commit }} - echo environment: ${{ github.event.inputs.environment }} + echo ref: ${{ inputs.ref }} + echo commit: ${{ inputs.commit }} + echo environment: ${{ inputs.environment }} + + publish: + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'fermyon' }} + outputs: + oci_ref: ${{ steps.publish-immutable.outputs.oci_ref }} + steps: + - uses: actions/checkout@v3 + + - name: Check out specific ref + if: ${{ github.event_name == 'workflow_dispatch' }} && ${{ inputs.ref != ''}} + run: git checkout ${{ inputs.ref }} + + - name: Check out specific commit + if: ${{ github.event_name == 'workflow_dispatch' }} && ${{ inputs.commit != ''}} + run: git checkout ${{ inputs.commit }} + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Setup Spin + uses: fermyon/actions/spin/setup@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + # TODO: update to next release version that includes https://github.com/fermyon/spin/pull/1296 + version: canary + + - name: Install npm packages + run: | + npm ci + + # TODO: any other node-y things to do prior to publish? 'npm run styles', etc.? + + - name: Create search index + run: | + npm run build-index + + - name: Log in to ${{ env.OCI_REGISTRY }} + uses: docker/login-action@v2.1.0 + with: + registry: ${{ env.OCI_REGISTRY }} + username: fermyon + password: ${{ secrets.DOCKERHUB_PAT }} + + - name: Publish Spin app - mutable tag + if: ${{ github.ref == 'refs/heads/main' }} + run: | + spin registry push ${{ env.OCI_REGISTRY }}/${{ env.OCI_IMAGE }}:latest + + - name: Publish Spin app - immutable tag + id: publish-immutable + shell: bash + run: | + image_tag="${{ github.ref_name }}-${{ github.sha }}" + sanitized_tag="${image_tag/\//-}" + export OCI_REF="${{ env.OCI_REGISTRY }}/${{ env.OCI_IMAGE }}:${sanitized_tag}" + echo "oci_ref=${OCI_REF}" >> $GITHUB_OUTPUT + spin registry push ${OCI_REF} deploy: runs-on: ubuntu-latest + needs: publish if: ${{ github.repository_owner == 'fermyon' }} steps: - uses: actions/checkout@v3 @@ -56,10 +119,8 @@ jobs: unzip nomad_${NOMAD_VERSION}_linux_$(dpkg --print-architecture).zip -d /usr/local/bin chmod +x /usr/local/bin/nomad - # This action currently generates a warning due to using deprecated features. - # https://github.com/aws-actions/configure-aws-credentials/issues/521 tracks the new behaviour. - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v2 with: role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.INFRA_NAMESPACE }}-${{ secrets.AWS_REGION }}-gha-certs role-session-name: fermyon-developer-deploy @@ -92,9 +153,6 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' }} shell: bash run: | - echo "GIT_REF=${{ github.event.inputs.ref }}" >> $GITHUB_ENV - echo "GIT_SHA=${{ github.event.inputs.commit }}" >> $GITHUB_ENV - if [[ "${{ github.event.inputs.environment }}" == "prod" ]]; then echo "PRODUCTION=true" >> $GITHUB_ENV echo "NOMAD_NAMESPACE=prod" >> $GITHUB_ENV @@ -107,36 +165,17 @@ jobs: if: ${{ github.event_name == 'push' }} shell: bash run: | - echo "GIT_REF=${{ github.ref }}" >> $GITHUB_ENV - echo "GIT_SHA=${{ github.sha }}" >> $GITHUB_ENV - - echo "PRODUCTION=true" >> $GITHUB_ENV - echo "NOMAD_NAMESPACE=prod" >> $GITHUB_ENV + # TODO: change back to prod defaults + # echo "PRODUCTION=true" >> $GITHUB_ENV + # echo "NOMAD_NAMESPACE=prod" >> $GITHUB_ENV + echo "PRODUCTION=false" >> $GITHUB_ENV + echo "NOMAD_NAMESPACE=staging" >> $GITHUB_ENV - name: Deploy shell: bash run: | - set -euox pipefail - - # purge any lingering/completed publish jobs - nomad job inspect publish-${{ env.JOB }} &>/dev/null && \ - nomad stop -purge -yes publish-${{ env.JOB }} - - # run the publish job - nomad run \ - -var "region=${{ secrets.AWS_REGION }}" \ - -var "git_ref=${{ env.GIT_REF }}" \ - -var "commit_sha=${{ env.GIT_SHA }}" \ - deploy/publish-${{ env.JOB }}.nomad - - # wait for publish job to complete - timeout 300s bash -c 'until [[ "$(nomad job inspect publish-${{ env.JOB }} | jq -j '.Job.Status')" == "dead" ]]; do sleep 2; done' - - readonly bindle_id="$(nomad logs -job publish-${{ env.JOB }} | sed -n 's/pushed: //p')" - - # run/update the website job nomad run \ -var "region=${{ secrets.AWS_REGION }}" \ -var "production=${{ env.PRODUCTION }}" \ - -var "bindle_id=${bindle_id}" \ - deploy/${{ env.JOB }}.nomad + -var "oci_ref=${{ needs.publish.outputs.oci_ref }}" \ + deploy/fermyon-developer.nomad diff --git a/.gitignore b/.gitignore index 1c1eca4b3..1b5f582b7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ node_modules broken_links final_broken .spin +# The search index is generated at time of deploy +static/data.json diff --git a/deploy/README.md b/deploy/README.md index 21e47b856..e8235ea5b 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,6 +1,12 @@ # Deployments -The [Developer](https://developer.fermyon.com) website is deployed via the [deploy.yaml](../.github/workflows/deploy.yml) GitHub workflow. +The [Developer](https://developer.fermyon.com) website is deployed via the [deploy.yml](../.github/workflows/deploy.yml) GitHub workflow. + +## Publishing + +In advance of deployment, the Spin app for this website is published to an OCI registry. + +In the case of publishing from the `main` branch, both a mutable tag and an immutable tag is pushed: `latest` and `main-` respectively. ## Auto Deploys @@ -8,12 +14,11 @@ The production version of the website is deployed whenever commits are pushed to ## Manual Deploys -Deployments may also be [triggered manually](https://github.com/fermyon/developer/actions/workflows/deploy.yml), providing a choice of `ref`, `sha` and `environment` (eg canary or prod). +Deployments may also be [triggered manually](https://github.com/fermyon/developer/actions/workflows/deploy.yml), providing a choice of git +`ref`, `commit` and `environment` (eg canary or prod). -## Nomad jobs +## Nomad job We currently deploy the website via its Nomad job directly. (In the future, we envision running the website as a Fermyon Cloud app.) -The [publish-fermyon-developer](./publish-fermyon-developer.nomad) Nomad job checks out this repo's source code and publishes it to Bindle. - -The [fermyon-developer](./fermyon-developer.nomad) Nomad job contains configuration for the running website, including the bindle ID to run from. \ No newline at end of file +The [fermyon-developer](./fermyon-developer.nomad) Nomad job contains configuration for the running website, including the OCI reference to run from. \ No newline at end of file diff --git a/deploy/fermyon-developer.nomad b/deploy/fermyon-developer.nomad index 95bc8f847..00de56fb5 100644 --- a/deploy/fermyon-developer.nomad +++ b/deploy/fermyon-developer.nomad @@ -35,11 +35,10 @@ EOF error_message = "The Let's Encrypt env must be either 'staging' or 'prod'." } } - -variable "bindle_id" { +variable "oci_ref" { type = string - default = "fermyon-developer/0.1.0" - description = "A bindle id, such as foo/bar/1.2.3" + default = "fermyon/developer:latest" + description = "The OCI reference of the Spin app for the Fermyon Developer website" } locals { @@ -99,15 +98,14 @@ job "fermyon-developer" { driver = "exec" artifact { - source = "https://github.com/fermyon/spin/releases/download/v0.10.1/spin-v0.10.1-linux-amd64.tar.gz" + source = "https://github.com/fermyon/spin/releases/download/v1.1.0/spin-v1.1.0-linux-amd64.tar.gz" options { - checksum = "sha256:105054335fd76b3d2a1b76a705dbdb3b83d7e4093b302a7816ce7f922893f29d" + checksum = "sha256:13ecd7be7fb3a054f41b72d65fd6648cd8221e5df57c6694f1a8e5532b79040d" } } env { RUST_LOG = "spin=trace" - BINDLE_URL = "http://bindle.service.consul:3030/v1" BASE_URL = "https://${local.hostname}" } @@ -115,8 +113,8 @@ job "fermyon-developer" { command = "spin" args = [ "up", + "--from", var.oci_ref, "--listen", "${NOMAD_IP_http}:${NOMAD_PORT_http}", - "--bindle", var.bindle_id, "--log-dir", "${NOMAD_ALLOC_DIR}/logs", "--temp", "${NOMAD_ALLOC_DIR}/tmp", diff --git a/deploy/publish-fermyon-developer.nomad b/deploy/publish-fermyon-developer.nomad deleted file mode 100644 index 00f13ef82..000000000 --- a/deploy/publish-fermyon-developer.nomad +++ /dev/null @@ -1,78 +0,0 @@ -variable "region" { - type = string -} - -variable "git_ref" { - type = string - default = "refs/heads/main" - description = "Git ref to use for the fermyon/developer repo clone. Default: refs/heads/main" -} - -variable "commit_sha" { - type = string - default = "" - description = "Specific commit SHA to check out. Default: empty/none" -} - -job "publish-fermyon-developer" { - type = "batch" - datacenters = [ - "${var.region}a", - "${var.region}b", - "${var.region}c", - "${var.region}d", - "${var.region}e", - "${var.region}f" - ] - - group "publish-fermyon-developer" { - count = 1 - - task "publish-fermyon-developer" { - driver = "exec" - - artifact { - source = "https://github.com/fermyon/spin/releases/download/v0.10.1/spin-v0.10.1-linux-amd64.tar.gz" - options { - checksum = "sha256:105054335fd76b3d2a1b76a705dbdb3b83d7e4093b302a7816ce7f922893f29d" - } - } - - env { - BINDLE_URL = "http://bindle.service.consul:3030/v1" - } - - template { - data = <<-EOF - #!/bin/bash - set -euo pipefail - - readonly repo_dir="${NOMAD_ALLOC_DIR}/fermyon-developer" - - # Capture branch/tag name from full ref - readonly branch="$(echo ${var.git_ref} | cut -d'/' -f3-)" - - # Directory and contents may be non-empty if this job is retrying while the - # bindle server is still coming up. (git clone will complain) - rm -rf ${repo_dir} - git clone -b ${branch} https://github.com/fermyon/developer.git ${repo_dir} - cd ${repo_dir} - - # Check out commit if provided - [[ "${var.commit_sha}" == "" ]] || git checkout ${var.commit_sha} - - ${NOMAD_TASK_DIR}/spin bindle push \ - -f spin.toml \ - -d ./staging_dir \ - --buildinfo "g$(git rev-parse HEAD)-$(date '+%Y%m%d%M%H%M%S')" - EOF - destination = "${NOMAD_TASK_DIR}/publish.bash" - perms = "700" - } - - config { - command = "${NOMAD_TASK_DIR}/publish.bash" - } - } - } -} \ No newline at end of file diff --git a/static/data.json b/static/data.json deleted file mode 100644 index cb10f1329..000000000 --- a/static/data.json +++ /dev/null @@ -1 +0,0 @@ -[{"project":"bartholomew","title":"Bartholomew configuration","subheading":"","content":"undefinedundefinedundefinedundefined","keywords":"install","subsectionKeywords":"","url":"/bartholomew/configuration"},{"project":"bartholomew","title":"Bartholomew configuration","subheading":"Configuration Using TOML","content":"undefined is a simple configuration format.\nBartholomew uses TOML for undefined as well as\nin the site configuration. In this chapter, we will focus on site configuration.Your site's config/ directory has one configuration file in it, called site.toml:title = \"Bartholomew\"\n# logo = \"URL to logo\"\nbase_url = \"http://localhost:3000\"\nabout = \"This site is generated with Bartholomew, the Spin micro-CMS. And this message is in site.toml.\"\ntheme = \"fermyon\"\nindex_site_pages = [\"main\"]\n\n[extra]\ncopyright = \"The Site Authors\"\ngithub = \"https://github.com/technosophos/bartholomew\"\ntwitter = \"https://twitter.com/technosophos\"","keywords":"","subsectionKeywords":"","url":"/bartholomew/configuration.md#configuration-using-toml"},{"project":"bartholomew","title":"Bartholomew configuration","subheading":"Your Site's Header; A.K.A. Frontmatter","content":"You can think of this as \"header for your site\".It has a few pre-defined fields:undefinedundefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/configuration.md#your-sites-header-aka-frontmatter"},{"project":"bartholomew","title":"Bartholomew configuration","subheading":"Extras","content":"You can define your own fields in the [extra] section. Anything in [extra] is not\nused by the system itself. But it's a useful way to pass information from one central\nplace to all of your templates. For example, a template can access the copyright value\nusing {{site.info.extra.copyright}}.","keywords":"","subsectionKeywords":"","url":"/bartholomew/configuration.md#extras"},{"project":"bartholomew","title":"Bartholomew configuration","subheading":"Next Steps - Using Themes","content":"Let's take a look at how you can configure your site to use undefined.","keywords":"","subsectionKeywords":"","url":"/bartholomew/configuration.md#next-steps---using-themes"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"","content":"undefinedWe are delighted that you are interested in making Bartholomew better! Thank you! We welcome and appreciate contributions of all types — opening issues, fixing typos, adding examples, one-liner code fixes, tests, or complete features. This document will guide you through making your first contribution to the project.Any contribution and interaction on any Fermyon project MUST follow our undefined. Thank you for being part of an inclusive and open community!To contribute to the Bartholomew project, please follow these steps.undefinedThe first step in contributing to Bartholomew is to create a fork of the GitHub repository. Let's get started.","keywords":"install","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Create a Fork of the Bartholomew GitHub Repository","content":"undefinedEnsure that you are forking Bartholomew to undefined; where you have full editing privileges.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#create-a-fork-of-the-bartholomew-github-repository"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Clone the Fork","content":"Go ahead and clone the new fork that you just created (the one which resides in your own GitHub account).undefinedCloning is performed using the following commands:# Change into home directory\n$ cd ~\n# Clone\n$ git clone git@github.com:yourusername/bartholomew.gitChange into the new Bartholomew directory (repo):$ cd ~/bartholomew","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#clone-the-fork"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Building the Bartholomew Server","content":"Rust and the wasm32-wasi target are prerequisites that are required to build Bartholomew from the source code. Please see the next section if you require these prerequisites on your system.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#building-the-bartholomew-server"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Prerequisites (Rust, wasm32-wasi and wasm-opt)","content":"You will need Rust installed.\nYou also need to install and add wasm32-wasi target once you have Rust installed.\nLastly, for prerequisites, you will need wasm-opt.undefinedInstalling Rust via the official Rust installation tool is recommended.undefinedInstalling the wasm32-wasi target is done using the following commands:$ rustup target install wasm32-wasi\n$ rustup target add wasm32-wasiIf you would like more details about the WebAssembly System Interface (WASI) there is an official specification and an 'intro' document available.undefinedPlease go to the undefined of binaryen and download the compressed package that suits your specific operating system. Unpack the compressed file to a location of your choice. We are just using ~/ (home directory) for presentation purposes. For example:# Download binaryen tar.gz file to home directory first\n# Then change into home directory\n$ cd ~\n# Unpack .tar.gz\n$ tar -zxvf binaryen-version_109-arm64-macos.tar.gzOnce unpacked, ensure that binaryen's bin folder is in your path. For example, open your .zshrc or .bash_profile file (depending on your OS) for editing:{{ tabs \"os\" }}{{ startTab \"Linux\"}}$ vi ~/.bash_profile{{ blockEnd }}{{ startTab \"macOS\"}}$ vi ~/.zshrc{{ blockEnd }}{{ blockEnd }}Then add the path to binaryen's bin folder to the last line of your file like this:$ export PATH=\"${HOME}/binaryen-version_109/bin:${PATH}\"Run whichever file you just edited. For example:{{ tabs \"os\" }}{{ startTab \"Linux\"}}$ . ~/.bash_profile{{ blockEnd }}{{ startTab \"macOS\"}}$ . ~/.zshrc{{ blockEnd }}{{ blockEnd }}You can check this has worked by echoing your path. For example:$ echo $PATHThe output from the above command will be similar to the following:~/binaryen-version_109/bin:/other/things/in/your/pathOnce the prerequisites are satisfied, you can go ahead and build Bartholomew:$ make buildThe make build command does a cargo build --target wasm32-wasi --release.undefinedThe above make build command creates a bartholomew.wasm file in the bartholomew/target/wasm32-wasi/release directory. You will need to copy that file into Bartholomew's modules/` directory for Spin to run Bartholomew.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#prerequisites-rust-wasm32-wasi-and-wasm-opt"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Building the Bartholomew Command Line Interface (CLI) Tool","content":"The Bartholomew Command Line Interface (CLI) Tool is called bart. this CLI is different from what we just built above. The CLI is a useful tool that can be used to create a new blog post using a single-line command and more. Installing the Bartholomew CLI is worth it, so take a few seconds to make it happen.To build the Bartholomew CLI from source, perform the following commands:$ cd ~/bartholomew\n$ make bartOnce built, you will find the very useful bart CLI executable in the ~/bartholomew/target/release directory.For more information about how to use the CLI, please type ~/bartholomew/target/release/bart --help, as shown below:$ ~/bartholomew/target/release/bart --help \nbart 0.6.0\nThe Bartholomew CLI\n\nUSAGE:\n bart \n\nFLAGS:\n -h, --help Prints help information\n -V, --version Prints version information\n\nSUBCOMMANDS:\n calendar Print the content calendar for a Bartholomew website\n check Check content and identify errors or warnings\n help Prints this message or the help of the given subcommand(s)\n new Create a new page or website from a templateThe Bartholomew CLI also has some other great features i.e. the bart command can automatically check the content (i.e. parse your web page and/or blog post markdown files) and identify & report any issues. See the example of test output below.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#building-the-bartholomew-command-line-interface-cli-tool"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Check Web Pages","content":"# Note the \"bart check\" command is being run, as an absolute path, from where we installed bartholomew from source (notice how we are passing in the \"content/*\" as a parameter to bart's \"check\" subcommand).\n$ ~/bartholomew/target/release/bart check content/*The output from the above command will be similar to the following (depending on the web pages on your system):✅ content/about.md","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#check-web-pages"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Check Blog Posts","content":"# Note the \"bart check\" command is still being run, as an absolute path, from where we installed bartholomew from source (notice how we are passing in the \"blog/*\" as a parameter to bart's \"check\" subcommand this time around).\n$ ~/bartholomew/target/release/bart check blog/*The output from the above command will be similar to the following (depending on the blog posts on your system):✅ content/blog/2022-02-08-hello-world.md\n✅ content/blog/why-and-how-wasm-cms-bartholomew.md","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#check-blog-posts"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"The Relationship Between Bartholomew and Spin","content":"To run Bartholomew, you will need a Spin-capable runtime.For Spin, follow undefined which details how to either:undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#the-relationship-between-bartholomew-and-spin"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"The Relationship Between Bartholomew and the Spin Fileserver","content":"Bartholomew uses an external file server called undefined. This file server facilitates the presentation of files to the end users. For example, whilst the web page contents (HTML) are generated dynamically upon request, image elements in that HTML page source can point to images that are stored on the host server. The Spin file server ensures that these static images are correctly served to the client's web browser (as per the HTML's requirements.)Please build the spin-fileserver and then copy the resulting spin_static_fs.wasm file into Bartholomew's modules/ directory. This will ensure that you are running the latest release of the spin-fileserver.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#the-relationship-between-bartholomew-and-the-spin-fileserver"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Create a New Branch","content":"Create a new branch that will house all of your changes for this specific contribution:$ git checkout -b my_new_branch","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#create-a-new-branch"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Create a New Remote","content":"Create a new remote for the upstream (a pointer to the original repository to which you are contributing):$ git remote add upstream https://github.com/fermyon/bartholomew","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#create-a-new-remote"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Modify Your Code","content":"Now is the time to make any changes to the code base and/or the documentation. This can include anything from updating files, creating new folders/files, writing documentation, writing tests, adding images and so much more.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#modify-your-code"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Collaborating When Submitting Your Code And/or Content","content":"Keep in mind that the developers who potentially merge your changes into the original repository will thoroughly check all of your work. There may be some back and forth as the final touches are added and your contribution is polished in readiness to deploy. Fermyon proudly hosts a undefined document. Taking a few minutes of your valuable time to view this page would be greatly appreciated.Also, when contributing, please ensure that you notice any unwritten conventions which are obvious. For example, if an entire folder of images is named using underscores i.e. image_1.png, image_2.png ensure that you don't upload an image with a different format like Image3.png or image-4.png. The same applies to written code, keep an eye out for conventions such as camel case and so forth when creating new variables. This will surely make the collaboration process smoother and faster.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#collaborating-when-submitting-your-code-andor-content"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Testing Your Changes","content":"Once you have made the required changes, test the code to make sure all the test cases are satisfied. You can test the code using the following command in the top-level directory, under which your changes exist:$ make test","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#testing-your-changes"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Viewing Your Changes","content":"If you made changes that will have a visual effect when Bartholomew has hosted i.e. changes to page layout and so forth, you can run the Bartholomew instance and view your changes on localhost. When you navigate to http://localhost:3000, you should see the website running.Running Bartholomew using Spin:$ spin upRunning Bartholomew using make:$ make serveFor convenience, make serve builds the code, and then runs spin up.undefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#viewing-your-changes"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Preview Mode","content":"If you have created changes to a web page or a blog post on a Bartholomew instance, you can preview your changes. For example, if you are contributing to the official documentation and want to render the site locally to preview your changes, you can navigate to the bartholomew/docs directory and run the site in preview mode, as shown below.undefinedundefinedundefinedTo view unpublished content i.e. if you are creating a new file, turn on PREVIEW_MODE.Spin:$ spin up -e PREVIEW_MODE=1Make:$ PREVIEW_MODE=1 make serve","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#preview-mode"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"The Page Cache","content":"Unless the environment variable -e DISABLE_CACHE=1 is set, the first load of a site will create a cache of page metadata in config/_cache.json. This is an optimization to reduce the number of file IO operations Bartholomew needs to make. If you are actively developing content, we suggest setting DISABLE_CACHE=1. By default, the Makefile's make serve target disables the cache, as make serve is assumed to be used only for developers.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#the-page-cache"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Adding, Committing and Pushing via Github","content":"Once you are satisfied with your contribution, please ensure that your GitHub installation is configured sufficiently so that you can --signoff as part of the git commit command. For example, please ensure that the user.name and user.email are configured in your terminal. You can check if these are set by typing git config --list.If you need to set these values please use the following commands:git config user.name \"yourname\"\ngit config user.email \"youremail@somemail.com\"More information can be found at this GitHub documentation page called undefined.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#adding-committing-and-pushing-via-github"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Add Changes","content":"Move to a top-level directory, under which your changes exist i.e. cd ~/bartholomew.Add your changes using the following command:$ git add .","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#add-changes"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Commit Changes","content":"Type the following commit command to ensure that you sign off (--signoff), sign the data (-S) - recommended, and also leave a short message (-m):$ git commit -S --signoff -m \"Your message here\"undefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#commit-changes"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Push Changes","content":"At this stage, it is a good idea to just quickly check what GitHub thinks the origin is. For example, if we type git remote -v we can see that the origin is our repo; which we a) forked the original repo into and b) which we then cloned to our local disk so that we could edit:$ git remote -v\norigin\tgit@github.com:yourusername/bartholomew.git (fetch)\norigin\tgit@github.com:yourusername/bartholomew.git (push)Next, we push the changes (explicitly mentioning the origin and also the new branch which we created in one of the earlier steps in this tutorial):$ git push -u origin my_new_branch","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#push-changes"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Create a Pull Request (PR)","content":"If you return to your GitHub repository in your browser, you will notice that a PR has automatically been generated for you.undefinedClicking on the green \"Compare and pull request\" button will allow you to add a title and description as part of the PR. You can also add any information in the textbox provided below the title. For example, screen captures and/or code/console/terminal snippets of your contribution working correctly and/or tests passing etc.There is one final step (another green button to push) ... Create Pull Request!Once you have finished creating your PR, please keep an eye on the PR; answering any questions as part of the collaboration process.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#create-a-pull-request-pr"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Merged","content":"The final stage of a successful contribution will be a notification that the PR has been merged.undefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#merged"},{"project":"bartholomew","title":"Contributing to Bartholomew","subheading":"Thank You","content":"At this point, you have performed a significant amount of work which is greatly appreciated.Thank you for contributing!Please keep in touch and contribute again in the future. We would love to see you back here.","keywords":"","subsectionKeywords":"","url":"/bartholomew/contributing-bartholomew.md#thank-you"},{"project":"bartholomew","title":"Introducing Bartholomew","subheading":"","content":"undefinedundefinedBartholomew is a simple CMS-like (Content Management System) tool for managing a\nwebsite. It is compiled to WebAssembly, and can run in any undefined\nenvironment.At a glance, with Bartholomew you can:undefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/bartholomew/index"},{"project":"bartholomew","title":"Introducing Bartholomew","subheading":"Overview","content":"Bartholomew is built using a Functions-as-a-Service (FaaS) model, similar to\none you might find in AWS Lambda or Azure Functions. The CMS is only running when\nit needs to handle incoming requests, reducing the load on the servers\nrunning it.Bartholomew is a undefined component, and\nwebsites built with Bartholomew are Spin applications that can run in any\nenvironment that is capable of running Spin. At Fermyon, we run all of our\nwebsites using Bartholomew and Spin, on our undefined.","keywords":"","subsectionKeywords":"","url":"/bartholomew/index.md#overview"},{"project":"bartholomew","title":"Introducing Bartholomew","subheading":"Taking Bartholomew for a Spin","content":"In the next page, we will undefined.","keywords":"","subsectionKeywords":"","url":"/bartholomew/index.md#taking-bartholomew-for-a-spin"},{"project":"bartholomew","title":"Markdown guide","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedTo write content for a Bartholomew website, there are two steps:undefinedundefined","keywords":"install","subsectionKeywords":"","url":"/bartholomew/markdown"},{"project":"bartholomew","title":"Markdown guide","subheading":"Creating a New File","content":"To create a new page, run the bart new post command, with the path to where\nthe file should be created as the first argument (it should be in the root or in\na subdirectory of content/) and the file name as the second argument, making\nsure the file has the .md extension.\nFor example, if the new post was created in content/foo.md, this file will be\nrendered by Bartholomew as /foo.undefinedBartholomew supports Markdown via the undefined library.","keywords":"","subsectionKeywords":"","url":"/bartholomew/markdown.md#creating-a-new-file"},{"project":"bartholomew","title":"Markdown guide","subheading":"Editing the Page Head","content":"The first part of any Bartholomew document is the undefined. You can think of every piece of content as having a undefined and a undefined. (Shoulders, knees, and toes will be added in a forthcoming release.)The head is formatted as TOML, which for the most part is just names and values.Here is an example head for a blog post:title = \"A New Article\"\ndescription = \"This article is really interesting and full of useful material.\"\ndate = \"2021-12-23T15:05:19Z\"\ntemplate = \"post\"\ntags = [\"news\", \"article\"]\nenable_shortcodes = false\n\n[extra]\nauthor = \"Matt Butcher\"\nauthor_page = \"/author/butcher\"\n---Here is an example head for a web page:title = \"The title\"\ndescription = \"A short description\"\ndate = \"2021-12-23T23:20:57Z\"\ntemplate = \"main\" # The default is `main`, which correlates to `templates/main.hbs`\n\n[extra]\nkey = \"your custom name value pairs go hear, but values MUST be strings\"\n---","keywords":"","subsectionKeywords":"","url":"/bartholomew/markdown.md#editing-the-page-head"},{"project":"bartholomew","title":"Markdown guide","subheading":"Markdown Body","content":"Markdown support includes all the usual stuff plus fenced codeblocks. Image links are\nsupported, but you need to use the external undefined\nlibrary to display the images. If you are using your deployment of Spin and Bartholomew, you can undefined from source.The last line of the example above is very important. The --- tells Bartholomew that the head is done, and the body is coming up.Every head must have a title. It is undefined recommended that it also have a date as well because dates are tied to some of Bartholomew's features. There are several other defined fields. When you want to add your own fields, put them after the [extra] marker.\nYou can add your own fields as name/value pairs. Just make sure you quote these string values.The following fields are defined for Bartholomew:undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedAfter the ---, you can write the content's body in Markdown.","keywords":"","subsectionKeywords":"","url":"/bartholomew/markdown.md#markdown-body"},{"project":"bartholomew","title":"Markdown guide","subheading":"Writing a Markdown Body","content":"undefined When writing a blog post with more than one paragraph, the syntax must be placed between the end of the first paragraph and the beginning of the second paragraph. The paragraph content above the will be shown in the HTML as having a slightly larger font size (relative to the rest of the page's content). The first paragraph will also appear when viewing the list of all blog posts (from the blog index page). Here is an example:This is my first paragraph which introduces my blog post.\n\n\n\nThis is the second paragraph, note the line breaks used in this code block example.\n\nThis is the third paragraph, it is treated exactly like the second paragraph.As you would have already seen, all documents in Bartholomew are written in Markdown.undefined is a simple text format designed to make it easy (and very fast) to write the text that can then be converted into HTML.For example, the Markdown *hello* is transformed into undefined. And the Markdown [example](http://example.com) is transformed into the link undefined.To make a header, you just use hash marks: # for a title, ## for a sub-header, and so on. Bullet lists are just plain text hyphen or asterisk lists.For example:- This\n- Is\n- A\n- ListThe above produces:undefinedundefinedundefinedundefinedMake text italic by wrapping it in underscores: _hello_ becomes undefined. And use double asterisks for bold. **goodbye** becomes undefined.There are other Markdown goodies, but one you should know is how to make a link. Links are built by putting text in square brackets, and the URL in parentheses:Say [Hello](http://example.com)The above markdown code will produce the following output:Say undefined.If you want to reference an image, just add ! in front of the square bracket. In that situation, the text in the square brackets becomes the image description.","keywords":"","subsectionKeywords":"","url":"/bartholomew/markdown.md#writing-a-markdown-body"},{"project":"bartholomew","title":"Markdown guide","subheading":"Why Is Markdown the Only Supported Format?","content":"Some static site generators allow you to use other formats like asciidoc. Bartholomew only supports Markdown. The reason for this is pragmatic: We are trying to keep the binary size as small as we can to make Bartholomew faster.","keywords":"","subsectionKeywords":"","url":"/bartholomew/markdown.md#why-is-markdown-the-only-supported-format"},{"project":"bartholomew","title":"Markdown guide","subheading":"Can I Embed HTML?","content":"Yes, you can embed HTML tags inside of your Markdown.Bartholomew supports reusable content using snippets in the form of shortcodes, so let's take a look at the undefined support in Bartholomew.Lastly, let's take a look at how templating is used in Bartholomew.","keywords":"","subsectionKeywords":"","url":"/bartholomew/markdown.md#can-i-embed-html"},{"project":"bartholomew","title":"Markdown guide","subheading":"Templates","content":"Bartholomew uses a undefined.\nThe template syntax is described in the undefined.Every file in the templates/ directory will be compiled into a template. The file is then accessible by its relative name, minus the extension. For example. templates/main.hbs will be accessible as main.Note that Bartholomew undefined to find a template named main. This template is used as a default when the content head does not contain a template directive. It is also used when an error occurs. You must have a main template.","keywords":"","subsectionKeywords":"","url":"/bartholomew/markdown.md#templates"},{"project":"bartholomew","title":"Markdown guide","subheading":"Accessing The Head (Front Matter) and the Body","content":"The head is available in the template using the {{ page.head }} object. For example, to print the title, use {{ page.head.title }}. To access your custom [extra] field named foo, use {{ page.head.extra.foo }}. The body is injected into the template and converted to HTML. That is, the template does not have access to the Markdown version of the document.To print the HTML body without having the output escaped, use {{{ page.body }}} (note the triple curly braces).","keywords":"","subsectionKeywords":"","url":"/bartholomew/markdown.md#accessing-the-head-front-matter-and-the-body"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedThis is a quickstart example of using Spin to deploy a Bartholomew CMS instance, locally on your machine. In this quickstart session, we use a Bartholomew site template which contains some pre-built parts (for your convenience). If you would like to build all of the parts separately from source (Spin, Bartholomew, File Server), please see the undefined which dives a lot deeper than this quickstart example.","keywords":"install","subsectionKeywords":"","url":"/bartholomew/quickstart"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Getting the spin Binary","content":"For Spin, follow undefined which details how to either:undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#getting-the-spin-binary"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Templates","content":"This quickstart method uses a Bartholomew site template. So while we do require spin (as per the details above) everything else we need (to launch our Bartholomew CMS website) is packaged up in the Bartholomew site template. Including the bartholomew.wasm and the spin_static_fs.wasm files which take care of Bartholomew's business logic and undefined needs, respectively. We will start working with the template in the next section.","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#templates"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Use the Bartholomew Site Template","content":"We can now generate a new repository with the same directory and file structure as the aforementioned Bartholomew site template. Simply visit undefined on undefined. Then click on the green Use this template button and follow the prompts. This will create a new repository in your GitHub account.Here are some additional details about undefined if you are interested.undefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#use-the-bartholomew-site-template"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Fetch Your Site","content":"Clone the repository which you created in the previous step:$ git clone Navigate into your newly cloned repository:$ cd ","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#fetch-your-site"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Spin Up Your Site","content":"Issue the following spin up command to launch your site on localhost:$ spin up\nLogging component stdio to \".spin/logs/\"\n\nServing http://127.0.0.1:3000\nAvailable Routes:\n bartholomew: http://127.0.0.1:3000 (wildcard)\n fileserver: http://127.0.0.1:3000/static (wildcard)As shown in the output above when you navigate to http://127.0.0.1:3000, you should see the website running.","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#spin-up-your-site"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Creating Your Content","content":"Creating content is made easy with the Bartholomew Command Line Interface (CLI) tool. The Bartholomew CLI can create new blog posts in a single command and also validate the content (Markdown files) that you write. Let's go ahead and install the Bartholomew CLI.","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#creating-your-content"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Bartholomew CLI","content":"For the bart CLI, there are two options:undefinedundefined# Change to home directory\n$ cd ~\n# Clone Bartholomew repository\n$ git clone https://github.com/fermyon/bartholomew.git\n# Change into Bartholomew directory\n$ cd bartholomew\n# Build the Bartholomew CLI\n$ make bartundefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#bartholomew-cli"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Create Your First Blog Post","content":"Ensure you are in the root directory of your previously cloned Bartholomew site template repository:$ cd You are now ready to start adding content to your new website. You will recall that we installed the bart CLI in a previous step. We can use this CLI to create a new blog post page.Note below how we:undefinedundefinedundefinedFeel free to change these values when you run the command on your system:$ mkdir content/blog\n$ bart new post content/blog protons.md --author \"Enrico Fermi\" --template \"blog\" --title \"On the Recombination of Neutrons and Protons\"The output from the above command will look similar to the following:Wrote new post in file content/blog/protons.md","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#create-your-first-blog-post"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Validate Your Content","content":"If you would like to check the validity of your content, you can run the following bart check command. Notice how we specify the location of the content we want to check. In this case, we are checking all of the Markdown files in the blog directory:$ bart check content/blog/*undefinedIf your syntax in the .md file is undefined, you will receive an output similar to the following. Otherwise, you will receive an informative error message and a cross:✅ content/blog/protons.md","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#validate-your-content"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Validating Content With Shortcodes","content":"If some of your documents use shortcodes, then the shortcodes directory must be specified via the --shortcodes flag, as shown below:$ bart check content/blog/* --shortcodes ./shortcodes","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#validating-content-with-shortcodes"},{"project":"bartholomew","title":"Taking Bartholomew for a spin","subheading":"Viewing Your Changes","content":"Running the spin up command from above (again) will render the post content at http://localhost:3000/blog/protonsundefinedNext, let's explore how to undefined.","keywords":"","subsectionKeywords":"","url":"/bartholomew/quickstart.md#viewing-your-changes"},{"project":"bartholomew","title":"Scripting guide","subheading":"","content":"undefinedundefinedSometimes you want to do something special in your templates. Perhaps it's some\nfancy formatting or iterating through some content and finding specific information.\nBartholomew provides the Rhai scripting language for this.undefined is a simple scripting language that has many stylistic similarities to\nRust, Go, and Python. The integration with Bartholomew is easy to work with.","subsectionKeywords":"","url":"/bartholomew/scripting"},{"project":"bartholomew","title":"Scripting guide","subheading":"The Basic","content":"undefinedundefinedundefinedundefinedFor example, let's take a look at the scripts/echo.rhai script. Note that because\nit is named echo.rhai, it will be accessible inside of templates as echo (just remove\nthe .rhai):let msg = params[0];\n\n\"hello \" + msg;On the first line, we accept the first parameter from the template (params[0]) and\nassign it to the variable named msg.undefinedThe second line returns the string \"hello\" concatenated with the value of msg.\nThat's all there is to a simple Rhai script.From a template, we can then use this script like this:{{ echo \"world\" }}When we run the template, we will see:hello world","keywords":"","subsectionKeywords":"","url":"/bartholomew/scripting.md#the-basic"},{"project":"bartholomew","title":"Scripting guide","subheading":"A More Complicated Example.","content":"Let's take a look at scripts/blogs.rhai, which is called in a template as blogs.\nThis script makes a list of all of the blog posts for the site:// Param 1 should be `site.pages`\nlet pages = params[0];\n\n// Loop through them and return all of the page objects that are in\n// the blog path. We want the results in an array to preserve ordering.\nlet blog_pages = [];\n\n// Get each blog post, assigning it to {path: object}.\nlet keys = pages.keys();\nfor item in keys {\n if item.index_of(\"/content/blog/\") == 0 {\n // Remove /content and .md\n let path = item.sub_string(8);\n path = path.sub_string(0, path.index_of(\".md\"));\n\n // Build an object that has `uri` and `page` keys.\n blog_pages.push(#{\n uri: path,\n page: pages[item],\n });\n //blog_pages[path] = pages[item];\n }\n \n}\n// Newest to oldest, assuming you put the date in the URI\nblog_pages.reverse();\n\n// Return the array of blog pages\nblog_pagesAgain note that this is one long procedural script that starts by fetching a parameter\nfrom the params array, and ends by sending back the output of the last line, blog_pages.The script returns a more complex data type, so let's see how this one is used in the\ncontent_sidebar.hbs template:
\n

Recent Posts

\n
    \n {{#each (blogs site.pages)}}
  1. {{page.head.title}}
  2. \n {{/each }}\n
\n
The code {{#each (blogs site.pages)}} calls blogs with param[0] set to site.pages.\nThen the each loops through the results.The value of this within the #each loop is the object that we created in Rhai:{\n uri: \"/some/path\"\n page: #{head: #{...}, body: \"some html\" }\n}So {{page.head.title}} will use this.uri, and the title\nfrom the page object.That's how you can use Rhai to add custom formatters to the site.No site would be complete without quality content, so let's take a look at the undefined which will help you create and format your awesome content.","keywords":"","subsectionKeywords":"","url":"/bartholomew/scripting.md#a-more-complicated-example"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"","content":"undefinedundefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/bartholomew/seo"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Google Verification Using Bartholomew","content":"Your Bartholomew implementation can be officially verified with Google. This is an important first step because the verification process provides you with access to the Google Search Console. The Google Search Console (formerly Google Webmaster Tools) allows webmasters to check indexing status, search queries, crawling errors and also optimize visibility of their site.Let's take a look at how the verification process is accomplished using Bartholomew.","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#google-verification-using-bartholomew"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Markdown","content":"The first step in the Google verification process is where Google provides you (the owner) of the specific website with a specially named file i.e. abcdefg.html. Google now wants you, the owner, to make this file available on your site, so that Google can fetch it as proof that you are the site's owner which has access control to the site. This is a really simple task. First you create a Markdown file (in Bartholomew's content directory) called abcdefg.md (the name of this file just has to match the name of the file which Google provided). undefined.Bartholomew uses templating so you just have to be explicit about a couple of things inside that new .md file. Specifically, Make sure that there is a template name (we will create the template next) and that the content type (of this file) is rendered as text/html. This is shown in the source code of the new abcdefg.md file below:title = \"Google Verification\"\ndescription = \"Google verification file which provides us with access to Google Search Console\"\ndate = \"2022-07-11T00:01:01Z\"\ntemplate = \"google_verification\"\ncontent_type = \"text/html\"\n---\n\nThis is the abcdefg.html file that Google can see openly at the root of the website.","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#markdown"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Template","content":"Bartholomew uses Handlebars templating. Therefore the next step is for you to go ahead and create a new google_verification.hbs file in Bartholomew's template directory. Once the file is created, populate it with the content which Google requested, below is just an example:{{! \nFor info on what can be placed here, see https://support.google.com/webmasters/answer/9008080#html_verification&zippy=%2Chtml-file-upload\n}}\ngoogle-site-verification: abcdefg.htmlAt this point the verification button in the Google dashboard can be pressed; Google fetches the file from your site and the verification is complete!","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#template"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Search Engine Optimization (SEO) Using Bartholomew","content":"In addition to just verifying the ownership of a site/domain, You can see that there are specific SEO requirements in relation to how the Googlebot indexes content. Googlebot is the web crawler software used by Google.Let's take a look at how the SEO compliance (i.e. sitemap and robots.txt) is accomplished using Bartholomew.","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#search-engine-optimization-seo-using-bartholomew"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Generating a Sitemap","content":"Google expects the standard sitemap protocol to be implemented. Thankfully, Bartholomew automatically builds a sitemap file based on the entire set of content in the CMS. The heavy lifting of the work is performed using the Rhai scripting language. Here is an example of the sitemap.rhai file that you would store in Bartholomew's scripts directory:// This function lists all of the posts, filtering a few.\n//\n// It returns an array of objects of the form:\n// [\n// #{ uri: \"path/to/page\", page: PageObject }\n// ]\n\n// These should be skipped.\nlet disallow = [\n \"/sitemap\", // Don't list self.\n // \"/tag\", // tag will list all of the tags on a site. If you prefer this not be indexed, uncomment this line.\n \"/index\", // This is a duplicate of /\n \"/atom\",\n \"/robots\",\n];\n\n// Param 1 should be `site.pages`\nlet pages = params[0];\n\nlet site_pages = [];\nlet keys = pages.keys();\nfor item in keys {\n let path = item.sub_string(8);\n let page = pages[item];\n\n path = path.sub_string(0, path.index_of(\".md\"));\n if !disallow.contains(path) {\n site_pages.push(#{\n uri: path,\n page: page,\n priority: prioritize(path),\n frequency: \"weekly\",\n });\n }\n \n}\n\n// This is an example of how we could prioritize based on information about the page.\n//\n// Specifically, here we use path to boost docs and blogs while reducing the priority\n// of author pages and features.\nfn prioritize(path) {\n let boost = [\"/blog/\", \"/docs/\"];\n for sub in boost {\n if path.contains(sub) {\n return 0.8\n }\n }\n let nerf = [\"/author/\", \"/features/\"];\n for sub in nerf {\n if path.contains(sub) {\n return 0.3\n }\n }\n 0.5\n}\n\n// Return the blogs sorted newest to oldest\nfn sort_by_date(a, b) {\n if a.page.head.date < b.page.head.date {\n 1\n } else {\n -1\n }\n}\n\n// Sort by the value of the page date.\nsite_pages.sort(Fn(\"sort_by_date\"));\n\nsite_pagesWhen Bartholomew sees an incoming request for the sitemap.xml URL, it will look inside the scripts directory for a Rhai file named sitemap.rhai (as shown above) and execute the script on demand.In conjunction to the above scripting, the aforementioned Handlebars templating assists in this work being performed dynamically (using variables common between the script and the template); as shown in the sitemap.hbs file's contents below:\n{{!\n For sitemap.xml, see https://www.sitemaps.org/protocol.html\n For date/time format, see https://www.w3.org/TR/NOTE-datetime\n}}\n\n \n {{site.info.base_url}}/\n daily\n 0.8\n \n {{#each (sitemap site.pages) }}\n \n {{../site.info.base_url}}{{uri}}\n {{#if page.head.date }}{{date_format \"%Y-%m-%dT%H:%M:%SZ\" page.head.date}}{{/if}}\n {{frequency}}\n {{priority}}\n \n {{/each}}\nundefinedFrom a display point of view we again just use Markdown (create a sitemap.md file in the site's content directory, correctly reference the name of the template (sitemap) and then ensure that the content type is set to text/xml). The above process will generate an XML sitemap called sitemap.xml at the root of the site:title = \"Sitemap XML file\"\ndescription = \"This is the sitemap.xml file\"\ndate = \"2021-12-29T22:36:33Z\"\ntemplate = \"sitemap\"\ncontent_type = \"text/xml\"\n---\n\nThis is the autogenerated sitemap. Note that the suffix .xml is replaced with .md by Bartholomew.","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#generating-a-sitemap"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Creating a Robots File","content":"You can control the Googlebot and tell it which files it may access on the site. This is done via the use of a robots.txt file.Similarly to the process above, you create a robots.md Markdown file in the content directory and also a robots.hbs in the template directory. These are shown below (in that order).title = \"Robots\"\ndescription = \"This is the robots.txt file\"\ndate = \"2021-12-30T03:17:26Z\"\ntemplate = \"robots\"\ncontent_type = \"text/plain\"\n---\n\nThis is the robots.txt file. It is autogenerated.{{! \nFor info on what can be placed here, see http://www.robotstxt.org/\nSee also: https://developers.google.com/search/docs/advanced/robots/intro\n}}\nUser-agent: *\nSitemap: {{site.info.base_url}}/sitemap.xml\nDisallow: /index","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#creating-a-robots-file"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Google Search Console","content":"The above steps of a) verifying and b) complying to the SEO requirements will give you great control over what is indexed by the Googlebot, and other Web Crawlers also. From a Google Search Console perspective specifically, you can now enjoy specific features and benefits such as on-demand page indexing i.e. allowing Google to go ahead and index specific content (like a new blog post) and much more.","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#google-search-console"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Google Search Analytics","content":"Google Analytics tracks and reports web site traffic. Showing not only where users are visiting from but how long they are staying and which pages they are reading and so forth. Once Google Analytics is configured you can even see how many users are on the site in real time. It is recommended that Google Analytics be used in conjunction with the above SEO.","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#google-search-analytics"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Rich Results","content":"Implementing rich results will make your listings stand out from other listings. Events, job listings, COVID-19 announcements and recipes all qualify to use rich results (a structured data markup approach to content). Whilst rich results can make your content more visually appealing, implementing rich results can also help search engines to crawl and rank your content which may result in higher rankings.","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#rich-results"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Structured Data","content":"Rich results are created using structured data inside your content. For example, JSON-LD, a JSON-based serialization for linked data.","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#structured-data"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Video","content":"Let's go ahead and create a JSON-LD code snippet for a video. In this case, we go straight to Google Search Console Documentation, specifically the JSON-LD Video Object section and read about the properties i.e. name, description, contentUrl and so forth.undefinedThe following is an example of the JSON-LD that we used (in a particular blog post).undefinedOnce the rich results code is created we can simply embed it into our Bartholomew blog post's Markdown file.","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#video"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Testing Rich Results","content":"When you are finished adding the rich media, you can go ahead and make sure that the content passes the \"rich results test\" undefined. This is a free online tool, which allows you to test both public URLs to content and also pasted-in HTML source code.Here is an example of how the code block from above is rendering in the site's HTML and therefore passing the rich results test.undefinedThe test only takes a couple of seconds, and as you can see we have successfully added rich result video data to our blog post.undefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#testing-rich-results"},{"project":"bartholomew","title":"Search Engine Optimization (SEO)","subheading":"Monitoring Rich Results","content":"Once deployed be sure to check your rich result undefined. The status reports essentially monitor the health of your existing pages, on an ongoing basis. A great way to quickly check that everything is still working as time goes by","keywords":"","subsectionKeywords":"","url":"/bartholomew/seo.md#monitoring-rich-results"},{"project":"bartholomew","title":"Bartholomew Shortcodes","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedShortcodes are simple reusable snippets can be used inside the markdown content.Bartholomew supports shortcodes simplify sharing content between different markdown files.","subsectionKeywords":"","url":"/bartholomew/shortcodes"},{"project":"bartholomew","title":"Bartholomew Shortcodes","subheading":"The Basic","content":"undefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/shortcodes.md#the-basic"},{"project":"bartholomew","title":"Bartholomew Shortcodes","subheading":"Creating a Shortcode","content":"An example of a shortcode is in shortcodes/alert.rhai. The shortcode is available in the markdown file as alert just like for scripts:let type = params[0];\nlet msg = params[1];\n\n\nlet colors = #{\n primary:`alert-primary`,\n success:`alert-success`,\n warning: `alert-warning`,\n danger: `alert-danger`,\n};\n\nlet icons = #{\n primary:`#info-fill`,\n success:`#check-circle-fill`,\n warning: `#exclamation-triangle-fill`,\n danger: `#exclamation-triangle-fill`,\n};\n`\n \n \n \n \n \n \n \n \n \n\n\n
\n \n
\n` + msg + `\n
\n
\n`","keywords":"","subsectionKeywords":"","url":"/bartholomew/shortcodes.md#creating-a-shortcode"},{"project":"bartholomew","title":"Bartholomew Shortcodes","subheading":"Enabling Shortcodes","content":"To enable shortcodes support for a particular document, the value of enable_shortcodes = true must be set in the page head:title = \"A New Article\"\ndescription = \"This article is really interesting and full of useful material.\"\ndate = \"2021-12-23T15:05:19Z\"\n...\n...\nenable_shortcodes = true\n---","keywords":"","subsectionKeywords":"","url":"/bartholomew/shortcodes.md#enabling-shortcodes"},{"project":"bartholomew","title":"Bartholomew Shortcodes","subheading":"Using Shortcodes","content":"Now the shortcode can be used in the markdown file by calling it with the required arguments. For the alerts script, this is the type of alert and the message to be displayed:\\{{ alert \"warning\" \"Bartholomew is a work in progress\" }}Which renders as the following:{{ alert \"warning\" \"Bartholomew is a work in progress\" }}","keywords":"","subsectionKeywords":"","url":"/bartholomew/shortcodes.md#using-shortcodes"},{"project":"bartholomew","title":"Bartholomew Shortcodes","subheading":"Note While Using Shortcodes","content":"An important note to be considered while using shortcodes is that the \\{{ }} must be escaped if they are not meant to be executed. This is only required in the content files where shortcodes are enabled.To escape the code, the \\ is used like\n{{{{raw}}}}\n\\\\{{ alert \"warning\" \"Bartholomew is a work in progress\" }}\n{{{{/raw}}}}\nwhich will render \\{{ alert \"warning\" \"Bartholomew is a work in progress\" }} instead of running the shortcode","keywords":"","subsectionKeywords":"","url":"/bartholomew/shortcodes.md#note-while-using-shortcodes"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"","content":"undefinedundefinedundefinedIn Bartholomew, layout is handled via templates. All templates are in the\ntemplates/ directory.undefined is a simple template language well-tuned to HTML.\nWhile the version of Handlebars used in Bartholomew is written in Rust, not JavaScript,\nit works almost identically.","subsectionKeywords":"","url":"/bartholomew/templates"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"A Simple Template","content":"Here is a simple HTML template with Handlebars:\n\n\n\n {{page.head.title}}\n\n\n\n {{{page.body}}}\n\n\nThe above sets the HTML document's title to whatever is in page.head.title, and\nthen fills in the body with the value of page.body.Let's take a brief look at the page object to understand what is happening here.","keywords":"","subsectionKeywords":"","url":"/bartholomew/templates.md#a-simple-template"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"The page Object","content":"In JSON, the page object looks like this:{\n head: {\n title: \"Some title\",\n description: \"Some description\",\n template: \"an optional template rather than using main.hbs\"\n extra: {\n \"key\": \"value\",\n \"description\": \"whatever is in the [extra] section of your Markdown doc's header\"\n }\n },\n body: \"

Some rendered Markdown content

\",\n published: true\n}To access a part, you simply use a dotted path notation. So to get the value of key in\nthe extra section, we use {{ page.head.extra.key }}.","keywords":"","subsectionKeywords":"","url":"/bartholomew/templates.md#the-page-object"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"The site Object","content":"In addition to the page object, there is also a site object. site.pages contains the head section and content of every page in the site. site.pages is only populated for templates included in index_site_pages in site.toml as described in the undefined:{\n info: {\n title: \"site title\"\n about: \"Site about information\"\n base_url: \"http://localhost:3000\"\n extra: {\n copyright: \"site-wide copyring (this is not required, since it's in extra)\"\n }\n },\n pages: [\n {...},\n {...}\n ]\n}Note that the site.pages array has access to every single document in the content folder.\nThis part of the API may change in the future, as it does not scale terribly well.","keywords":"","subsectionKeywords":"","url":"/bartholomew/templates.md#the-site-object"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"The env Object","content":"The third top-level object is env, which holds all of the environment data.The env object is a set of keys and values:{\n PREVIEW_MODE: \"0\"\n ...\n}You can dump the entire contents of env using a template like this:
    \n {{#each env}}\n
  • {{@key}}: \"{{this}}\"
  • \n {{/each}}\n
","keywords":"","subsectionKeywords":"","url":"/bartholomew/templates.md#the-env-object"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"The request Object","content":"The fourth top-level object is request, which holds all the details about\nthe HTTP request, the path of this resource, and other Spin information.The request object is a set of keys and values:{\n spin-full-url: \"http://localhost:3000/test\"\n ...\n}You can dump the entire contents of request using a template like this:
    \n {{#each request}}\n
  • {{@key}}: \"{{this}}\"
  • \n {{/each}}\n
","keywords":"","subsectionKeywords":"","url":"/bartholomew/templates.md#the-request-object"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"Including a Template","content":"It is possible to include a template into another template.\nFor example, if we want to include the navbar.hbs template, we use a \"partial\" include\nlike this:{{> navbar }}Note that we drop the .hbs suffix when including this way.","keywords":"","subsectionKeywords":"","url":"/bartholomew/templates.md#including-a-template"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"Calling Template Helpers","content":"There are a few template helpers defined in Bartholomew.For example, to change a piece of text to all-caps, use the upper helper:{{ upper \"hello\" }}The above will render HELLO.Note that you can create custom template helpers using undefined.","keywords":"","subsectionKeywords":"","url":"/bartholomew/templates.md#calling-template-helpers"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"Defined Helper Functions","content":"The following helper functions are provided with Bartholomewundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/templates.md#defined-helper-functions"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"Values Reference","content":"The following values are available in the template. This is formatted in YAML for readability. The four top-level objects are:undefinedundefinedundefinedundefinedTo reference a particular value, use dotted notation. For example, page has a head, which has the page's title.\nTo access the title, use {{ page.head.title }}:# Page holds data specific to the page that matches the URI. For example, the URI /foo loads page data from /content/foo.md\npage:\n # The info from the TOML head:\n head:\n date: \"2022-01-11T20:08:47Z\" # In templates, use `date_format \"%B %m, %Y\" page.head.date` to format\n title: \"WebAssembly Language Support Matrix\" # The H1 title\n description: \"Tracking the programming languages that compile to WebAssembly (Wasm).\"\n template: \"page\"\n tags: [\"webassembly\", \"programming languages\", \"javascript\", \"python\", \"rust\", \"dotnet\", \"ruby\"]\n\n extra: # Remember that everything in extra is optional, so wrap in #if\n author: \"Fermyon Staff\"\n author_page: \"/author/\"\n image: \"/static/some/thing.png\"\n last_modified: \"2022-01-11T20:08:47Z\" # Only if the page is modified\n body: \"The HTML content\"\n published: true # Set to false if either manually disabled by author or if `date` is in the future\n\n# Site holds sitewide data\nsite:\n # Info keeps all of the information from `site.toml`\n info:\n title: \"The site title\"\n base_url: \"https://something\" # Note no trailing slash\n about: \"Pithy site summary\"\n extra:\n your_defined_field: \"Value\"\n\n pages: # Metadata for every page on the site. Note that there is no guarantee that `body` will be set.\n \"/foo\":\n head:\n date: \"2022-01-11T20:08:47Z\"\n title: \"Some title\"\n # These are all optional\n description: \"Some Description (if set)\"\n tags: [\"tags\", \"if\", \"set\"]\n template: \"template-if-set\"\n # user-defined extras go here\n extras:\n any: \"extras go here\"\n published: true\n \"/bar\":\n head:\n # These fields will always be set.\n date: \"2022-01-11T20:08:47Z\"\n title: \"Some other title\"\n published: true\n\nenv: # Environment variables set in the Bartholomew Wasm module\n\nrequest: # HTTP request data along with spin information.Given the above, for example, you can write a template to create a link to the current page:{{page.head.title}}The above will output something like:WebAssembly In Action","keywords":"","subsectionKeywords":"","url":"/bartholomew/templates.md#values-reference"},{"project":"bartholomew","title":"Bartholomew templates","subheading":"Template Functions and Rhai Scripts","content":"The following template functions are built into Bartholomew:undefinedundefinedundefinedundefinedNext, let's dive into undefined.","keywords":"","subsectionKeywords":"","url":"/bartholomew/templates.md#template-functions-and-rhai-scripts"},{"project":"bartholomew","title":"Bartholomew themes","subheading":"","content":"undefinedundefinedundefinedundefinedBartholomew supports theming which allows for easy customization of the site along with the user-defined tempaltes.","subsectionKeywords":"","url":"/bartholomew/themes"},{"project":"bartholomew","title":"Bartholomew themes","subheading":"Adding a Theme","content":"Once the initial site has been set up using the undefined, create a themes folder where you will be able to download different themes:$ mkdir themesOnce the folder is created, different themes can be added as submodules to the folder which can then in turn be used to theme the site:$ cd themes\n$ git submodule add Multiple themes can be added to the themes directory but only one of them will be active at a given time as described in the next section.","keywords":"","subsectionKeywords":"","url":"/bartholomew/themes.md#adding-a-theme"},{"project":"bartholomew","title":"Bartholomew themes","subheading":"Configuring the Site to Use the Theme","content":"To choose a theme for the website, the theme attribute in config/site.toml must be configured, where the value is the name of the theme as found in the themes/ folder:title = \"Bartholomew Documentation\"\nbase_url = \"http://localhost:3000\"\nabout = \"The Micro-CMS for WebAssembly and Spin\"\ntheme = \"\"\n\n[extra]\ncopyright = \"Fermyon\"\ngithub = \"https://github.com/fermyon/bartholomew\"\ntwitter = \"https://twitter.com/fermyontech\"\nga_measurement_id = \"\"\n\ndate_style = \"%B %e, %Y\"One more step that needs to be done before themes are fully available to the site is to change the static file server component in the spin.toml configuration so that it provides the static assets of the selected theme. The convention of mounting the static assets of the themes before the user-defined static assets is recommended:[[component]]\nsource = \"modules/spin_static_fs.wasm\"\nid = \"fileserver\"\nfiles = [ {source = \"themes//static\", destination =\"/\"}, { source = \"static/\", destination = \"/\" }, ]\n[component.trigger]\nroute = \"/static/...\"","keywords":"","subsectionKeywords":"","url":"/bartholomew/themes.md#configuring-the-site-to-use-the-theme"},{"project":"bartholomew","title":"Bartholomew themes","subheading":"Template Precedence","content":"When a theme is enabled for a site, both the user-defined and theme-provided assets like the templates, scripts and static assets will be available. If a theme-provided asset and user-provided asset have the same name, the user-defined asset takes precedence. This allows for the user to override the theme to allow for customization.As an example, if both templates/main.hbs and themes//templates/main.hbs exist, the user-defined templates/main.hbs takes precedence leading to the rendering engine using the user-defined template overriding the theme.","keywords":"","subsectionKeywords":"","url":"/bartholomew/themes.md#template-precedence"},{"project":"bartholomew","title":"Bartholomew themes","subheading":"Creating a Theme","content":"Creating a theme for Bartholomew is easy. Create a new folder and initialize it:$ mkdir custom_theme\n$ cd custom_theme\n$ git initOnce the git repository is initialized, create the three required directories:$ mkdir templates scripts staticCreate the custom theme by placing the handlebar templates in the template/ folder while the Rhai scripts are placed in the scripts/ folder. All the static assets such as the images, JS and CSS are placed in the static folder. For reference on creating templates, refer to the undefined.Once the required changes are done, commit and push the changes to a remote repository, so as to allow for the theme to cloned as a submodule that can be used for theming a site.Let's take a look at how you can do something special in your templates undefined","keywords":"","subsectionKeywords":"","url":"/bartholomew/themes.md#creating-a-theme"},{"project":"cloud","title":"Fermyon Cloud Key Value Store","subheading":"","content":"Fermyon Cloud now supports undefined. While Spin applications are well suited for event-driven, stateless workloads, these serverless workloads often rely on external services to persist state beyond the lifespan of a single request. With the introduction of undefined, you can now persist non-relational data in a key/value store that is always available for your serverless application (within milliseconds and without cold starts). No infrastructure provisioning or maintenance is required. Developers can now deploy their Fermyon Cloud Key Value Store applications simply by running spin cloud deploy.References:undefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/cloud/changelog/cloud-key-value"},{"project":"cloud","title":"Custom Fermyon Subdomains","subheading":"","content":"Fermyon Cloud users can now apply custom Fermyon subdomains to their Spin applications. By default, every Spin application recieves a domain name that has the following format: -.fermyon.app. With custom Fermyon subdomains, users can choose their preferred subdomain name to be appended to the .fermyon.app apex domain. To learn more, follow the undefined.References:undefinedundefined","subsectionKeywords":"","url":"/cloud/changelog/custom-fermyon-subdomain"},{"project":"cloud","title":"GitHub Action support for spin/deploy","subheading":"","content":"Fermyon has recently released a collection of undefined for working with Spin that will empower you to pick up your development speed:undefinedundefinedundefinedUsing spin/deploy, you can now set up a Continuous Deployment pipeline on Fermyon Cloud. Whenever you merge a pull request into your GitHub repository of choice, spin/deploy will trigger a new Spin application deployment. You can learn more about the set-up for this process with our undefined.References:undefinedundefined","subsectionKeywords":"","url":"/cloud/changelog/gh-actions-spin-deploy"},{"project":"cloud","title":"Request count metrics available in Cloud UI","subheading":"","content":"You can now see your Spin application’s request count over time in the Fermyon Cloud User Interface (UI). Request count is defined as the number of times your Spin application’s undefined has been called while running on Fermyon Cloud. To view an Spin application’s request count data over time, log into Fermyon Cloud and click on the application of interest. Feel free to reach out to us on undefined and let us know what you think.References:undefinedundefined","subsectionKeywords":"","url":"/cloud/changelog/request-metrics-in-cloud"},{"project":"cloud","title":"Apply Custom Fermyon Subdomain","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedEvery Spin application running on Fermyon Cloud receives a domain name that has the following format: .fermyon.app. For a more easily recognizable domain name, you may want to change your Spin application's domain name from slats-the-cat-o7jecuug.fermyon.app to slatsthecat.fermyon.app.Custom Fermyon subdomain names allow you to rename the subdomain. This custom Fermyon subdomain will be combined with the .fermyon.app apex domain to give your application a complete domain name.","subsectionKeywords":"","url":"/cloud/custom-fermyon-subdomain"},{"project":"cloud","title":"Apply Custom Fermyon Subdomain","subheading":"Prerequisites","content":"Log into undefined and ensure you have a Spin application running on Fermyon Cloud. If you do not have a Spin application yet, follow our undefined to deploy one.undefined","keywords":"","subsectionKeywords":"","url":"/cloud/custom-fermyon-subdomain.md#prerequisites"},{"project":"cloud","title":"Apply Custom Fermyon Subdomain","subheading":"Select Your Spin Application","content":"Select the application whose domain name you intend to modify. Then select the undefined button in the top right corner.undefined","keywords":"","subsectionKeywords":"","url":"/cloud/custom-fermyon-subdomain.md#select-your-spin-application"},{"project":"cloud","title":"Apply Custom Fermyon Subdomain","subheading":"Apply New Custom Fermyon Domain","content":"In the text box, you will see your application's current subdomain, followed by the apex domain fermyon.app.undefinedInput your preferred subdomain name that meets the following characteristics:undefinedundefinedundefinedThen click save to apply your changes.undefined","keywords":"","subsectionKeywords":"","url":"/cloud/custom-fermyon-subdomain.md#apply-new-custom-fermyon-domain"},{"project":"cloud","title":"Apply Custom Fermyon Subdomain","subheading":"Validate Custom Fermyon Subdomain Name","content":"If you view the application's domain name in the panel view, you should see it has been updated to reflect your custom Fermyon subdomain name.undefinedVisit the application's domain name to validate the change has been applied successfully.$ curl quickstart.fermyon.appundefined","keywords":"","subsectionKeywords":"","url":"/cloud/custom-fermyon-subdomain.md#validate-custom-fermyon-subdomain-name"},{"project":"cloud","title":"Apply Custom Fermyon Subdomain","subheading":"Next Steps","content":"Congratulations, you have successfully applied a custom Fermyon subdomain to your Spin application.undefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/custom-fermyon-subdomain.md#next-steps"},{"project":"cloud","title":"Persistent Data: PostgreSQL","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/cloud/data-postgres"},{"project":"cloud","title":"Persistent Data: PostgreSQL","subheading":"PostgreSQL","content":"undefined, is a powerful, open-source object-relational database system that has earned a strong reputation for reliability, robustness and performance. This tutorial will implement a persistent storage solution for Fermyon Cloud, using PostgreSQL. In this tutorial, we will be using undefined's free PostgreSQL service.","keywords":"","subsectionKeywords":"","url":"/cloud/data-postgres.md#postgresql"},{"project":"cloud","title":"Persistent Data: PostgreSQL","subheading":"Spin and Fermyon Cloud","content":"First, you need to have Spin installed on your computer. Please use the official Fermyon Cloud Quickstart to both undefined Spin and also undefined to Fermyon Cloud.","keywords":"","subsectionKeywords":"","url":"/cloud/data-postgres.md#spin-and-fermyon-cloud"},{"project":"cloud","title":"Persistent Data: PostgreSQL","subheading":"Using Spin Application Templates","content":"The Spin CLI facilitates the creation of new Spin applications through the use of application templates. You can install Spin application templates using the undefined. The template we are interested in, for this tutorial, is the experimental http-csharp template. We can go ahead and install it using the following command:$ spin templates install --git https://github.com/fermyon/spin-dotnet-sdk --branch main --updateThe output from the command above will be similar to the following:Copying remote template source\nInstalling template http-csharp...\nInstalled 1 template(s)\n\n+------------------------------------------------------------+\n| Name Description |\n+============================================================+\n| http-csharp HTTP request handler using C# (EXPERIMENTAL) |\n+------------------------------------------------------------+","keywords":"","subsectionKeywords":"","url":"/cloud/data-postgres.md#using-spin-application-templates"},{"project":"cloud","title":"Persistent Data: PostgreSQL","subheading":"Creating Our New Spin Application","content":"The official Spin CLI documentation also has instructions on how to undefined, from an existing template. Using the docs as a reference, we can perform the following:$ spin new http-csharp httpCSharpApplication\nProject description: A new http-csharp spin application\nHTTP base: /\nHTTP path: /data","keywords":"","subsectionKeywords":"","url":"/cloud/data-postgres.md#creating-our-new-spin-application"},{"project":"cloud","title":"Persistent Data: PostgreSQL","subheading":"CSharp","content":"using Fermyon.Spin.Sdk;\nusing System.Net;\nusing System.Text;\n\nnamespace httpCSharpApplication;\n\npublic static class Handler {\n [HttpHandler]\n public static HttpResponse HandleHttpRequest(HttpRequest request) {\n if (request.Url == Warmup.DefaultWarmupUrl) {\n return new HttpResponse();\n }\n\n var connectionString = \"user=username password=password dbname=databasename host=127.0.0.1\";\n var result = PostgresOutbound.Query(connectionString, \"SELECT * FROM myTable\");\n\n var responseText = new StringBuilder();\n\n responseText.AppendLine($\"Got {result.Rows.Count} row(s)\");\n responseText.AppendLine($\"COL: [{String.Join(\" | \", result.Columns.Select(FmtCol))}]\");\n\n string FmtEntry(DbValue v) {\n return v.Value() switch {\n null => \"\",\n var val => val.ToString() ?? \"\",\n };\n }\n\n foreach(var row in result.Rows) {\n responseText.AppendLine($\"ROW: [{String.Join(\" | \", row.Select(FmtEntry))}]\");\n }\n\n return new HttpResponse {\n StatusCode = HttpStatusCode.OK,\n BodyAsString = responseText.ToString(),\n };\n }\n private static string FmtCol(PgColumn c) {\n return $ \"{c.Name} ({c.DataType})\";\n }\n}","keywords":"","subsectionKeywords":"","url":"/cloud/data-postgres.md#csharp"},{"project":"cloud","title":"Persistent Data: PostgreSQL","subheading":"Wizer","content":"Wizer is required to successfully build this application. Please go ahead and install Wizer using the following command:$ cargo install wizer --all-features","keywords":"","subsectionKeywords":"","url":"/cloud/data-postgres.md#wizer"},{"project":"cloud","title":"Persistent Data: PostgreSQL","subheading":"Spin Build","content":"To build the application, use the following command:$ spin build","keywords":"","subsectionKeywords":"","url":"/cloud/data-postgres.md#spin-build"},{"project":"cloud","title":"Persistent Data: PostgreSQL","subheading":"Spin Deploy","content":"To deploy the application, use the deploy command:$ spin deployThe above deploy command will produce similar output to the following:Deployed httpCSharpApplication version 1.0.0+XXXXXXXX\nAvailable Routes:\n http_c_sharp_application: https://httpcsharpapplication-XXXXXXXX.fermyon.app/dataVisiting the above URL will show your data in the browser's body","keywords":"","subsectionKeywords":"","url":"/cloud/data-postgres.md#spin-deploy"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/cloud/data-redis"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Redis","content":"undefined, is an open-source data store used by millions of developers as a database, cache, streaming engine, and message broker. This tutorial will implement a persistent storage solution for Fermyon Cloud, using Redis. In this tutorial, we will be using undefined' free Redis service.","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#redis"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Spin and Fermyon Cloud","content":"First, you need to have Spin installed on your computer. Please use the official Fermyon Cloud Quickstart to both undefined Spin and also undefined to Fermyon Cloud.","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#spin-and-fermyon-cloud"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Using Spin Application Templates","content":"The Spin CLI facilitates the creation of new Spin applications through the use of application templates. You can install Spin application templates using the undefined. For example:$ spin templates install --git https://github.com/fermyon/spin --updateThe output from the command above will be similar to the following:Copying remote template source\n// --snip--\nInstalling template http-rust...\n// --snip--\n\nInstalled 8 template(s)\n\n+-----------------------------------------------------------------+\n| Name Description |\n+=================================================================+\n| // --snip-- |\n| http-rust HTTP request handler using Rust |\n+-----------------------------------------------------------------+The template we are interested in is http-rust.","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#using-spin-application-templates"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Creating Our New Spin Application","content":"The official Spin CLI documentation also has instructions on how to undefined. Let's go ahead and create a new http-rust application:$ spin new http-rust redisRustApplicationThe above command will ask you to enter a Project description, HTTP base (default set to / which is fine) and HTTP path:Project description: \nHTTP base: /\nHTTP path: /...You can fill out these values, when prompted, for example:Project description: A new redis-rust Spin Application.\nHTTP base: /\nHTTP path: /dataundefined","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#creating-our-new-spin-application"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Redis","content":"To create your free database go to undefined. For this tutorial, we are signing up using GitHub authentication.undefinedOnce you have logged in to Redislabs click on the Data Access Control and Databases menus (in the left sidebar) to create roles/users and a new Redis database. Be sure to acknowledge the usernames, passwords and database URLs (provided during setup) as we will be using these to configure our Spin application. Please see the undefined for additional information.","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#redis"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Configuration","content":"Open the Spin application's spin.toml file and add an environment configuration value, within the [[component]] section. For example:environment = { REDIS_ADDRESS = \"redis://username:password@redis.cloud.redislabs.com:16675\" }undefined","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#configuration"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Spin SDK's Redis Implementation","content":"In this tutorial we will create the code to store and retrieve data from a Redislabs database.","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#spin-sdks-redis-implementation"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Rust","content":"The following is the content which is required in the src/lib.rs file. Feel free to cut and paste the following, for convenience:use anyhow::{anyhow, Result};\nuse spin_sdk::{\n http::{Request, Response},\n http_component, redis,\n};\n\nconst REDIS_ADDRESS_ENV: &str = \"REDIS_ADDRESS\";\n\n#[http_component]\nfn publish(_req: Request) -> Result {\n let address = std::env::var(REDIS_ADDRESS_ENV)?;\n\n // Set the Redis key \"spin-example\" to value \"Eureka Cloud!\"\n redis::set(&address, \"spin-example\", &b\"Eureka Cloud!\"[..])\n .map_err(|_| anyhow!(\"Error executing Redis set command\"))?;\n\n // Get the value from the Redis key \"spin-example\"\n let payload =\n redis::get(&address, \"spin-example\").map_err(|_| anyhow!(\"Error querying Redis\"))?;\n\n // Return the permanently stored value to the user's browser body\n Ok(http::Response::builder()\n .status(200)\n .header(\"foo\", \"bar\")\n .body(Some(payload.into()))?)\n}","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#rust"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Log Into Fermyon Cloud","content":"We need to log into Fermyon Cloud, so that we can build/deploy our application. For example:$ spin login","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#log-into-fermyon-cloud"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Spin Build","content":"We build this application by typing the following command:$ spin buildThe output from the above command will look similar to the following:Executing the build command for component redis-rust-application: cargo build --target wasm32-wasi --release\n Finished release [optimized] target(s) in 0.09s\nSuccessfully ran the build command for the Spin components.","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#spin-build"},{"project":"cloud","title":"Persistent Data: Redis","subheading":"Spin Deploy","content":"To deploy the application, use the deploy command:$ spin deployThe above deploy command will produce similar output to the following:Deployed redisRustApplication version 0.1.0+XXXXXXXX\nApplication is running at redisRustApplication-XXXXXXXX.fermyon.appVisiting the URL, which is provided by the spin deploy command's output will show the Eureka Cloud! value for the spin-example key which is stored in Redis.","keywords":"","subsectionKeywords":"","url":"/cloud/data-redis.md#spin-deploy"},{"project":"cloud","title":"Deleting an application","subheading":"","content":"undefinedundefinedundefinedIt’s super easy to delete your application with Fermyon Cloud. This can be done in just a few steps using the Fermyon Cloud website.","subsectionKeywords":"","url":"/cloud/delete"},{"project":"cloud","title":"Deleting an application","subheading":"Sign in to the Fermyon Cloud Website","content":"undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/delete.md#sign-in-to-the-fermyon-cloud-website"},{"project":"cloud","title":"Deleting an application","subheading":"Delete Your Application","content":"undefinedundefinedundefinedundefinedundefinedundefinedThat’s all about deleting your Spin Application on the Fermyon Cloud!","keywords":"","subsectionKeywords":"","url":"/cloud/delete.md#delete-your-application"},{"project":"cloud","title":"Deleting an application","subheading":"Next Steps","content":"undefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/delete.md#next-steps"},{"project":"cloud","title":"Deploy an application","subheading":"","content":"undefinedundefinedundefinedundefinedThis article will guide you through deploying a Spin Application with the Fermyon Cloud. You can deploy your undefined or undefined site in just a few steps.","subsectionKeywords":"","url":"/cloud/deploy"},{"project":"cloud","title":"Deploy an application","subheading":"Prerequisites - Install the Spin CLI","content":"Before developing a Spin application, you need to have the Spin CLI installed locally. Here’s a way to install the Spin CLI:$ curl -fsSL https://developer.fermyon.com/downloads/install.sh | bash{{ details \"Additional info\" \"It's easier if you move the spin binary somewhere in your path, so it can be accessed from any directory. E.g., sudo mv ./spin /usr/local/bin/spin. \\n\\nYou can verify the version of Spin installed by running spin --version\" }}","keywords":"","subsectionKeywords":"","url":"/cloud/deploy.md#prerequisites---install-the-spin-cli"},{"project":"cloud","title":"Deploy an application","subheading":"Log in to the Fermyon Cloud","content":"Next, you can log in to the Fermyon Cloud, which requires your GitHub account to sign in:$ spin login\n\nCopy your one-time code:\n\nXXXXXXXX\n\n...and open the authorization page in your browser:\n\nhttps://cloud.fermyon.com/device-authorization\n\nWaiting for device authorization...\nDevice authorized!This command generates an authentication code for your device to be authorized on the Fermyon Cloud.","keywords":"","subsectionKeywords":"","url":"/cloud/deploy.md#log-in-to-the-fermyon-cloud"},{"project":"cloud","title":"Deploy an application","subheading":"Deploy Your Application","content":"After having signed in to the Fermyon Cloud, you deploy the application, by running the following command in the directory where your applications spin.toml file is located:$ spin deploy\nUploading cloud_start version 0.1.0+XXXXXXXX...\nDeploying...\nWaiting for application to become ready... ready\nAvailable Routes:\n cloud-start: https://cloud-start-XXXXXXXX.fermyon.app (wildcard)Take a look at the undefined for how to get a pre-built application to deploy.{{ details \"Additional info\" \"spin deploy can point to a spin.toml file by using the --file option.\" }}undefined","keywords":"","subsectionKeywords":"","url":"/cloud/deploy.md#deploy-your-application"},{"project":"cloud","title":"Deploy an application","subheading":"Next Steps","content":"undefined","keywords":"","subsectionKeywords":"","url":"/cloud/deploy.md#next-steps"},{"project":"cloud","title":"Deployment Concepts","subheading":"","content":"undefinedundefinedundefined","subsectionKeywords":"","url":"/cloud/deployment-concepts"},{"project":"cloud","title":"Deployment Concepts","subheading":"Deployments in Fermyon Cloud","content":"Deploying applications to a cloud service should be simple. Even though there is complexity involved in operating a cloud with many servers, many applications, and an ever-changing number of workloads, the user's responsibility when deploying their applications should be minimal.In this article, we describe the core technologies and concepts, which are part of the deployment process in the Fermyon Cloud.","keywords":"","subsectionKeywords":"","url":"/cloud/deployment-concepts.md#deployments-in-fermyon-cloud"},{"project":"cloud","title":"Deployment Concepts","subheading":"Bindle - An Aggregate Object Storage System","content":"The Fermyon Cloud uses undefined to package and distribute Spin applications. Bindle is an open-source project, built and maintained by Deis Labs. Bindle is very well documented, so we will not go into details of how Bindle works, other than calling out a few core features of the system here:undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/deployment-concepts.md#bindle---an-aggregate-object-storage-system"},{"project":"cloud","title":"Deployment Concepts","subheading":"The Deployment Process Explained","content":"In the Fermyon Cloud, we host an instance of Bindle, so when you run spin deploy, the command will take care of:undefinedundefinedThere is no direct interaction with Bindle when using the Fermyon Cloud.Let's unfold each of these steps.","keywords":"","subsectionKeywords":"","url":"/cloud/deployment-concepts.md#the-deployment-process-explained"},{"project":"cloud","title":"Deployment Concepts","subheading":"1. Packaging and Uploading an Application","content":"The first step in deploying an application is to package all the files into parcels and generate an invoice. All of this is handled in a staging directory, which is either a temporary directory using undefined or the staging directory defined using the --staging-dir option.The bindle will be named using the name from spin.toml, the version from spin.toml, and a build metadata string, which is automatically generated by Spin at deployment time.Bindles in the Fermyon Cloud always use Semantic Versioning and require major, minor, and patch version numbers to be specified. Therefore, the version in spin.toml has to conform to the MAJOR.MINOR.PATCH format, i.e. 1.0.1 is valid, 1.0 and 1 are not valid version numbers for a Spin application. The result is that the version of a Spin application will be like this my_app_name/1.0.0+r80e5abb.Following packaging, the bindle will be uploaded to the Fermyon Cloud. As soon as a bindle has been uploaded, it cannot be modified or deleted. This is to preserve the integrity of the immutability of bindles.","keywords":"","subsectionKeywords":"","url":"/cloud/deployment-concepts.md#1-packaging-and-uploading-an-application"},{"project":"cloud","title":"Deployment Concepts","subheading":"2. Create or Upgrade an Application","content":"The next step in the deployment process is to create or upgrade the application.An application in the Fermyon Cloud can have multiple revisions, which are tied to channels. These concepts are derived from undefined an open-source Platform as a Service (PaaS) for WebAssembly. As you deploy your application both an application, a channel and a revision will be created in the Fermyon Cloud.If the application already exists, an upgrade will take place. What happens at this point is that a new revision will be created, and as soon as this is deemed healthy, traffic will start to route to the new revision. The failover from the old to the new revision takes a short amount of time, during which you will be able to observe replies from both revisions. The application existence is determined based on the combination of the user account and the Spin application name, as defined in spin.toml.The deployment process checks for the application health endpoint and finishes once the application is concluded to be healthy by the cloud. The application health point is an integral part of the Fermyon Cloud but does reserve the HTTP route /.well-known/spin/health, which will not be routed to your Spin application.","keywords":"","subsectionKeywords":"","url":"/cloud/deployment-concepts.md#2-create-or-upgrade-an-application"},{"project":"cloud","title":"Develop a Spin application","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedThis article briefly describes how to create a new Spin application. For a more thorough guide to developing Spin applications, take a look undefined.","subsectionKeywords":"","url":"/cloud/develop"},{"project":"cloud","title":"Develop a Spin application","subheading":"Prerequisites - Install the Spin CLI","content":"Before developing a Spin application, you need to have the Spin CLI installed locally. Here’s a way to install the Spin CLI:","keywords":"","subsectionKeywords":"","url":"/cloud/develop.md#prerequisites---install-the-spin-cli"},{"project":"cloud","title":"Develop a Spin application","subheading":"Install Spin","content":"{{ tabs \"os\" }}{{ startTab \"Linux\"}}Download the spin binary using the install.sh script hosted on this site:Then move the spin binary somewhere in your path, so you can run it from anywhere. For example:$ sudo mv ./spin /usr/local/bin/spin{{ blockEnd }}{{ startTab \"macOS\"}}Download the spin binary using the install.sh script hosted on this site:Then move the spin binary somewhere in your path, so you can run it from anywhere. For example:$ sudo mv ./spin /usr/local/bin/spin{{ blockEnd }}{{ startTab \"Windows\"}}Download the Windows binary release of Spin from GitHub.Unzip the binary release and place the spin.exe in your system path.{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/cloud/develop.md#install-spin"},{"project":"cloud","title":"Develop a Spin application","subheading":"Install the Prerequisites","content":"","keywords":"","subsectionKeywords":"","url":"/cloud/develop.md#install-the-prerequisites"},{"project":"cloud","title":"Develop a Spin application","subheading":"Install a Template","content":"The quickest and most convenient way to start a new application is to use a Spin template. Let's install the templates for your preferred language.{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}$ spin templates install --git https://github.com/fermyon/spin --update\nCopying remote template source\nInstalling template redis-rust...\nInstalling template http-rust...\n... other templates omitted ...\n+------------------------------------------------------------------------+\n| Name Description |\n+========================================================================+\n| ... other templates omitted ... |\n| http-rust HTTP request handler using Rust |\n| redis-rust Redis message handler using Rust |\n| ... other templates omitted ... |\n+------------------------------------------------------------------------+Note: The Rust templates are in a repo that contains several other languages; they will all be installed together.{{ blockEnd }}{{ startTab \"TypeScript\" }}$ spin templates install --git https://github.com/fermyon/spin-js-sdk --update\nCopying remote template source\nInstalling template http-js...\nInstalling template http-ts...\n+------------------------------------------------------------------------+\n| Name Description |\n+========================================================================+\n| http-js HTTP request handler using Javascript |\n| http-ts HTTP request handler using Typescript |\n+------------------------------------------------------------------------+{{ blockEnd }}{{ startTab \"Python\" }}$ spin templates install --git https://github.com/fermyon/spin-python-sdk --update\nCopying remote template source\nInstalling template http-py...\n+---------------------------------------------+\n| Name Description |\n+=============================================+\n| http-py HTTP request handler using Python |\n+---------------------------------------------+{{ blockEnd }}{{ startTab \"TinyGo\" }}$ spin templates install --git https://github.com/fermyon/spin --update\nCopying remote template source\nInstalling template redis-go...\nInstalling template http-go...\n+------------------------------------------------------------------------+\n| Name Description |\n+========================================================================+\n| ... other templates omitted ... |\n| http-go HTTP request handler using (Tiny)Go |\n| redis-go Redis message handler using (Tiny)Go |\n| ... other templates omitted ... |\n+------------------------------------------------------------------------+Note: The Go templates are in a repo that contains several other languages; they will all be installed together.{{ blockEnd }}{{ blockEnd }}{{ details \"Additional info\" \"If you already have templates installed, you can update them by running spin templates install --git https://github.com/fermyon/spin --update.\" }}","keywords":"","subsectionKeywords":"","url":"/cloud/develop.md#install-a-template"},{"project":"cloud","title":"Develop a Spin application","subheading":"Install the Tools","content":"Some languages require additional tool support for Wasm.{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}You'll need the wasm32-wasi target for Rust:$ rustup target add wasm32-wasiundefined{{ blockEnd }}{{ startTab \"TypeScript\" }}You'll need the Spin js2wasm plugin:$ spin plugins update\n$ spin plugins install js2wasm --yesundefined{{ blockEnd }}{{ startTab \"Python\" }}You'll need the Spin py2wasm plugin:$ spin plugins update\n$ spin plugins install py2wasm --yesundefined{{ blockEnd }}{{ startTab \"TinyGo\" }}You'll need the TinyGo compiler, as the standard Go compiler does not yet support the WASI standard. See the undefined.undefined{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/cloud/develop.md#install-the-tools"},{"project":"cloud","title":"Develop a Spin application","subheading":"Create a New Spin Application From a Template","content":"Now you can create your very own application based on one of the templates you just installed. For this example, we'll show how to create a HTTP application.{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}Use the spin new command and the http-rust template to scaffold a new Spin application:$ spin new\nPick a template to start your application with:\n http-c (HTTP request handler using C and the Zig toolchain)\n http-csharp (HTTP request handler using C# (EXPERIMENTAL))\n http-go (HTTP request handler using (Tiny)Go)\n http-grain (HTTP request handler using Grain)\n> http-rust (HTTP request handler using Rust)\n http-swift (HTTP request handler using SwiftWasm)\n http-zig (HTTP request handler using Zig)\n redis-go (Redis message handler using (Tiny)Go)\n redis-rust (Redis message handler using Rust)\n\nEnter a name for your new application: hello_rust\nProject description: My first Rust Spin application\nHTTP base: /\nHTTP path: /...This command created a directory with the necessary files needed to build and run a Rust Spin application. Here's the spin.toml file, the manifest for a Spin application:spin_manifest_version = \"1\"\nauthors = [\"You \"]\ndescription = \"My first Rust Spin application\"\nname = \"hello_rust\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-rust\"\nsource = \"target/wasm32-wasi/release/hello_rust.wasm\"\nallowed_http_hosts = []\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"cargo build --target wasm32-wasi --release\"{{ blockEnd }}{{ startTab \"TypeScript\"}}Use the spin new command and the http-ts template to scaffold a new Spin application. (If you prefer JavaScript to TypeScript, the http-js template is very similar.)$ spin new\nPick a template to start your application with:\n http-js (HTTP request handler using Javascript)\n> http-ts (HTTP request handler using Typescript)\nEnter a name for your new application: hello_typescript\nProject description: My first TypeScript Spin application\nHTTP base: /\nHTTP path: /...This command created a directory with the necessary files needed to build and run a TypeScript Spin application. Here's the spin.toml file, the manifest for a Spin application:spin_manifest_version = \"1\"\nauthors = [\"You \"]\ndescription = \"My first TypeScript Spin application\"\nname = \"hello_typescript\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-typescript\"\nsource = \"target/spin-http-js.wasm\"\nexclude_files = [\"**/node_modules\"]\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"npm run build\"{{ blockEnd }}{{ startTab \"Python\"}}Use the spin new command and the http-py template to scaffold a new Spin application:$ spin new\nPick a template to start your application with:\n> http-py (HTTP request handler using Python)\nEnter a name for your new application: hello_python\nDescription: My first Python Spin application\nHTTP base: /\nHTTP path: /...This command created a directory with the necessary files needed to build and run a Python Spin application. Here's the spin.toml file, the manifest for a Spin application:spin_manifest_version = \"1\"\nauthors = [\"You \"]\ndescription = \"My first Python Spin application\"\nname = \"hello_python\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-python\"\nsource = \"app.wasm\"\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"spin py2wasm app -o app.wasm\"{{ blockEnd }}{{ startTab \"TinyGo\"}}Use the spin new command and the http-go template to scaffold a new Spin application:$ spin new\nPick a template to start your application with:\n http-c (HTTP request handler using C and the Zig toolchain)\n http-empty (HTTP application with no components)\n> http-go (HTTP request handler using (Tiny)Go)\n http-grain (HTTP request handler using Grain)\n http-php (HTTP request handler using PHP)\n http-rust (HTTP request handler using Rust)\nEnter a name for your new application: hello_go\nDescription: My first Go Spin application\nHTTP base: /\nHTTP path: /...This command created a directory with the necessary files needed to build and run a Go Spin application using the TinyGo compiler. Here's the spin.toml file, the manifest for a Spin application:spin_manifest_version = \"1\"\nauthors = [\"You \"]\ndescription = \"My first Go Spin application\"\nname = \"hello_go\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-go\"\nsource = \"main.wasm\"\nallowed_http_hosts = []\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go\"{{ blockEnd }}{{ blockEnd }}Next, let’s build the app:{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}$ spin build\nExecuting the build command for component hello-rust: cargo build --target wasm32-wasi --release\n Updating crates.io index\n Updating git repository `https://github.com/fermyon/spin`\n Updating git repository `https://github.com/bytecodealliance/wit-bindgen`\n Compiling anyhow v1.0.69\n Compiling version_check v0.9.4\n # ...\n Compiling spin-sdk v0.10.0 \n Compiling hello-rust v0.1.0 (/home/ivan/testing/start/hello_rust)\n Finished release [optimized] target(s) in 11.94s\nSuccessfully ran the build command for the Spin components.If the build fails, check:undefinedundefinedundefined{{ blockEnd }}{{ startTab \"TypeScript\"}}As normal for NPM projects, before you build for the first time, you must run npm install:$ npm install\n\nadded 134 packages, and audited 135 packages in 2s\n\n18 packages are looking for funding\n run `npm fund` for details\n\nfound 0 vulnerabilitiesThen run spin build:$ spin build\nExecuting the build command for component hello-typescript: npm run build\n\n> hello-typescript@1.0.0 build\n> npx webpack --mode=production && mkdir -p target && spin js2wasm -o target/spin-http-js.wasm dist/spin.js\n\nasset spin.js 4.57 KiB [emitted] (name: main)\nruntime modules 670 bytes 3 modules\n./src/index.ts 2.85 KiB [built] [code generated]\nwebpack 5.75.0 compiled successfully in 1026 ms\n\nStarting to build Spin compatible module\nPreinitiating using Wizer\nOptimizing wasm binary using wasm-opt\nSpin compatible module built successfully\nSuccessfully ran the build command for the Spin components.If the build fails, check:undefinedundefinedundefined{{ blockEnd }}{{ startTab \"Python\"}}$ spin build\nExecuting the build command for component hello-python: spin py2wasm app -o app.wasm\nSpin-compatible module built successfully\nSuccessfully ran the build command for the Spin components.If the build fails, check:undefinedundefined{{ blockEnd }}{{ startTab \"TinyGo\"}}$ spin build\nExecuting the build command for component hello-go: tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go\ngo: downloading github.com/fermyon/spin/sdk/go v0.10.0\nSuccessfully ran the build command for the Spin components.If the build fails, check:undefinedundefinedundefined{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/cloud/develop.md#create-a-new-spin-application-from-a-template"},{"project":"cloud","title":"Develop a Spin application","subheading":"Run the Application","content":"Now it’s time to spin up the application:$ spin up\nServing http://127.0.0.1:3000\nAvailable Routes:\n hello-rust: http://127.0.0.1:3000 (wildcard){{ details \"Additional info\" \"Spin prints application output, and any Spin errors, to the console. To see additional diagnostic information, set the RUST_LOG environment variable for detailed logs, before running spin up, e.g., RUST_LOG=spin=debug spin up.\" }}Spin will instantiate all components from the application manifest, and will create the router configuration for the HTTP trigger accordingly. The component can now be invoked by making requests to http://localhost:3000:$ curl -i localhost:3000\nHTTP/1.1 200 OK\nfoo: bar\ncontent-length: 15\n\nHello, Fermyon","keywords":"","subsectionKeywords":"","url":"/cloud/develop.md#run-the-application"},{"project":"cloud","title":"Develop a Spin application","subheading":"Next Steps","content":"undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/develop.md#next-steps"},{"project":"cloud","title":"FAQ and Known Limitations","subheading":"","content":"undefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/cloud/faq"},{"project":"cloud","title":"FAQ and Known Limitations","subheading":"Service Limits","content":"The following are the limits of the Fermyon Cloudundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/faq.md#service-limits"},{"project":"cloud","title":"FAQ and Known Limitations","subheading":"Known Limitations","content":"","keywords":"","subsectionKeywords":"","url":"/cloud/faq.md#known-limitations"},{"project":"cloud","title":"FAQ and Known Limitations","subheading":"Spin Limitations","content":"Fermyon Cloud supports Spin CLI v0.6.0 or newer. That being said, there are certain Spin SDK triggers and APIs that are not yet supported on Fermyon Cloud. Please review the table below to see what is supported today on Fermyon Cloud:| Feature | SDK Supported? |\n|-----|-----|\n| undefined |\n| undefined | Supported |\n| undefined | Not supported |\n| undefined |\n| undefined | Supported |\n| undefined | Supported (only default store) |\n| undefined | Supported |\n| undefined | Supported |\n| undefined | Supported |\n| undefined |\n| undefined | Not supported |To learn more about what feature support looks like for various programming languages, visit the undefined.","keywords":"","subsectionKeywords":"","url":"/cloud/faq.md#spin-limitations"},{"project":"cloud","title":"FAQ and Known Limitations","subheading":"Other Limitations","content":"undefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/faq.md#other-limitations"},{"project":"cloud","title":"FAQ and Known Limitations","subheading":"Frequently Asked Questions","content":"undefined11:08:13 : Hello from Rust\n11:08:18 : Hello from Rust - updated\n11:08:19 : Hello from Rust\n11:08:23 : Hello from Rust - updated\n11:08:24 : Bad Gateway\n11:08:26 : Hello from Rust - updated\n11:08:27 : Bad Gateway\n11:08:29 : Hello from Rust - updatedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/faq.md#frequently-asked-questions"},{"project":"cloud","title":"FAQ and Known Limitations","subheading":"Next Steps","content":"undefined","keywords":"","subsectionKeywords":"","url":"/cloud/faq.md#next-steps"},{"project":"cloud","title":"Fermyon Cloud","subheading":"","content":"undefinedundefined","subsectionKeywords":"","url":"/cloud/fermyon-cloud"},{"project":"cloud","title":"Fermyon Cloud","subheading":"The Fermyon Cloud Explained","content":"undefined is a cloud application platform for WebAssembly microservices. It enables you to run undefined, at scale, in the cloud, without any infrastructure setup.In this article we describe the core technologies and concepts which are part of the Fermyon Cloud.","keywords":"","subsectionKeywords":"","url":"/cloud/fermyon-cloud.md#the-fermyon-cloud-explained"},{"project":"cloud","title":"Fermyon Cloud","subheading":"Core Components of the Fermyon Cloud","content":"The Fermyon Cloud is an expansion of the undefined, built to provide scalable and resilient hosting for Spin applications.The following core components are needed in a cloud platform:undefinedundefinedundefinedundefinedundefinedAll of the above are key to ensuring the high dynamism of a cloud platform where applications and traffic come and go in unpredictable patterns.","keywords":"","subsectionKeywords":"","url":"/cloud/fermyon-cloud.md#core-components-of-the-fermyon-cloud"},{"project":"cloud","title":"Fermyon Cloud","subheading":"Orchestration","content":"The core orchestration in the Fermyon Cloud is done using undefined. Nomad enables the Fermyon Cloud to spread workloads across servers, and run an optimized highly resilient cloud. Nomad guarantees quick placement of Spin applications, at deployment, upgrade and failures in the underlying infrastructure - which do happen, so we planned for it.","keywords":"","subsectionKeywords":"","url":"/cloud/fermyon-cloud.md#orchestration"},{"project":"cloud","title":"Fermyon Cloud","subheading":"Service Resolution","content":"In order to successfully route traffic to your Spin applications, a service registry is needed to map application endpoints to sockets on servers. This is done using Consul, as an integrated part of Nomad. Consul helps ensure we can always find your application, as the orchestration engine may or may not move it around.","keywords":"","subsectionKeywords":"","url":"/cloud/fermyon-cloud.md#service-resolution"},{"project":"cloud","title":"Fermyon Cloud","subheading":"Traffic Routing","content":"Traffic routing is done using undefined, which ensures that data is sent to the right places in the cloud.","keywords":"","subsectionKeywords":"","url":"/cloud/fermyon-cloud.md#traffic-routing"},{"project":"cloud","title":"Fermyon Cloud","subheading":"Application Packaging and Distribution","content":"To package and distribute application to and within the cloud, we rely on Bindle. For more information about deployment concepts in the Fermyon Cloud, see undefined.","keywords":"","subsectionKeywords":"","url":"/cloud/fermyon-cloud.md#application-packaging-and-distribution"},{"project":"cloud","title":"Fermyon Cloud","subheading":"Web UI, API and CLI","content":"The Fermyon Cloud exposes a public undefined which is used by the undefined and the Spin CLI when logging in and deploying applications.","keywords":"","subsectionKeywords":"","url":"/cloud/fermyon-cloud.md#web-ui-api-and-cli"},{"project":"cloud","title":"Deploying Spin Apps Using GitHub Actions","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedGitHub Actions is a Continuous Integration and Continuous Deployment (CI/CD) platform for GitHub developers. With GitHub Actions, developers can create workflows that build, test and even deploy (to production) pull requests that have been merged into their repositories. Workflows are composed of actions, of which there are thousands, published by companies and individual enthusiasts alike, that automate otherwise mundane, repetitive in-house development tasks.Fermyon provides a set of actions for working with Spin. For example:undefinedundefinedundefinedIn this tutorial, you'll create an application and deploy it from GitHub to Fermyon Cloud using the fermyon/actions/spin/deploy action. Upon completing this tutorial, you should have a GitHub repository that builds and deploys a Spin application to Fermyon Cloud every time you merge a pull request to main. Let's get started!","subsectionKeywords":"","url":"/cloud/github-actions"},{"project":"cloud","title":"Deploying Spin Apps Using GitHub Actions","subheading":"Prerequisites","content":"To ensure the tutorial goes smoothly, please check you have the following:undefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/github-actions.md#prerequisites"},{"project":"cloud","title":"Deploying Spin Apps Using GitHub Actions","subheading":"Create a Spin Application in a GitHub Repository","content":"undefinedThe first step is to create a GitHub repository for your application. You can do this either on undefined or via the undefined using the gh repo create command. We'll use the GitHub CLI below for illustrative purposes.# Create the github-actions-tutorial repo on GitHub and clone a working copy.\n$ gh repo create github-actions-tutorial --public --cloneNow let's create a Spin HTTP application using a template.{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}$ spin new http-rust github-actions-tutorial --accept-defaults\n# Choose y at the prompt\ngithub-actions-tutorial already contains other files. Generate into it anyway? [y/n]undefined{{ blockEnd }}{{ startTab \"TypeScript\" }}$ spin new http-ts github-actions-tutorial --accept-defaults\n# Choose y at the prompt\ngithub-actions-tutorial already contains other files. Generate into it anyway? [y/n]undefined{{ blockEnd }}{{ startTab \"Python\" }}$ spin new http-py github-actions-tutorial --accept-defaults\n# Choose y at the prompt\ngithub-actions-tutorial already contains other files. Generate into it anyway? [y/n]undefined{{ blockEnd }}{{ startTab \"TinyGo\" }}$ spin new http-go github-actions-tutorial --accept-defaults\n# Choose y at the prompt\ngithub-actions-tutorial already contains other files. Generate into it anyway? [y/n]undefined{{ blockEnd }}{{ blockEnd }}Push your new application to GitHub:$ cd github-actions-tutorial\n$ git add .\n$ git commit -m \"Basic Spin application\"\n$ git push --set-upstream origin mainYou've now got a Spin application in GitHub, ready to add a deployment workflow.","keywords":"","subsectionKeywords":"","url":"/cloud/github-actions.md#create-a-spin-application-in-a-github-repository"},{"project":"cloud","title":"Deploying Spin Apps Using GitHub Actions","subheading":"Create a Personal Access Token","content":"When you deploy from the Spin command line, you log into Fermyon Cloud using your browser. Of course, that's not possible in an unattended environment like GitHub Actions. Instead, you'll use a undefined (PAT). Let's create one now.undefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/github-actions.md#create-a-personal-access-token"},{"project":"cloud","title":"Deploying Spin Apps Using GitHub Actions","subheading":"Save the Personal Access Token as a Repository Secret","content":"For your deployment workflow to use this new PAT, you must save it as a GitHub repository secret. The steps for this are different according to whether you prefer to use the GitHub Web user interface or the command line:{{ tabs \"gh-interact\" }}{{ startTab \"GitHub UI\"}}Open the GitHub Web site and navigate to your repository.Choose the Settings tab.In the left hand navigation bar, find the Security section and choose \"Secrets and variables\" > Actions.Choose the \"New repository secret\" button.In the Name box, enter FERMYON_CLOUD_TOKEN.In the Secret box, paste the token you copied above.undefined{{ blockEnd }}{{ startTab \"GitHub CLI\" }}Switch back to your command prompt. Be sure you are in the github-actions-tutorial working copy and run:$ gh secret set FERMYON_CLOUD_TOKEN\n? Paste your secretAt the prompt, paste the token you copied above, and press Enter.{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/cloud/github-actions.md#save-the-personal-access-token-as-a-repository-secret"},{"project":"cloud","title":"Deploying Spin Apps Using GitHub Actions","subheading":"Create the Deployment Workflow","content":"Finally it is time to create the deployment workflow! A workflow is stored as a YAML file in the .github/workflows directory.Make sure you are in your Spin application directory, and create the workflows directory:# Create workflows directory (and make parent directory as necessary via the -p option)\n$ mkdir -p .github/workflows/Now create a file named deploy.yml in that directory:# For the sake of example this invokes Visual Studio Code, but you\n# can use any text editor from vi to Notepad\n$ code .github/workflows/deploy.ymlThen add the following code to the deploy.yml file:{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}name: Deploy\n\n# Deploy only when a change is pushed or merged to `main`\non:\n push:\n branches:\n - main\n\njobs:\n spin:\n runs-on: ubuntu-latest\n name: Build and deploy\n steps:\n - uses: actions/checkout@v3\n\n - name: Install Rust\n uses: dtolnay/rust-toolchain@stable\n with:\n toolchain: 1.66\n targets: wasm32-wasi\n\n - name: Install Spin\n uses: fermyon/actions/spin/setup@v1\n\n - name: Build and deploy\n uses: fermyon/actions/spin/deploy@v1\n with:\n fermyon_token: $\\{{ secrets.FERMYON_CLOUD_TOKEN }}{{ blockEnd }}{{ startTab \"TypeScript\"}}name: Deploy\n\n# Deploy only when a change is pushed or merged to `main`\non:\n push:\n branches:\n - main\n\njobs:\n spin:\n runs-on: ubuntu-latest\n name: Build and deploy\n steps:\n - uses: actions/checkout@v3\n\n # TypeScript/JavaScript build requires the js2wasm plugin\n - name: Install Spin\n uses: fermyon/actions/spin/setup@v1\n with:\n plugins: js2wasm\n\n - name: Run npm install\n run: npm install\n\n - name: Build and deploy\n uses: fermyon/actions/spin/deploy@v1\n with:\n fermyon_token: $\\{{ secrets.FERMYON_CLOUD_TOKEN }}{{ blockEnd }}{{ startTab \"Python\"}}name: Deploy\n\n# Deploy only when a change is pushed or merged to `main`\non:\n push:\n branches:\n - main\n\njobs:\n spin:\n runs-on: ubuntu-latest\n name: Build and deploy\n steps:\n - uses: actions/checkout@v3\n\n # Python build requires the py2wasm plugin\n - name: Install Spin\n uses: fermyon/actions/spin/setup@v1\n with:\n plugins: py2wasm\n\n - name: Build and deploy\n uses: fermyon/actions/spin/deploy@v1\n with:\n fermyon_token: $\\{{ secrets.FERMYON_CLOUD_TOKEN }}{{ blockEnd }}{{ startTab \"TinyGo\"}}name: Deploy\n\n# Deploy only when a change is pushed or merged to `main`\non:\n push:\n branches:\n - main\n\njobs:\n spin:\n runs-on: ubuntu-latest\n name: Build and deploy\n steps:\n - uses: actions/checkout@v3\n\n - name: \"Install Go\"\n uses: actions/setup-go@v3\n with:\n go-version: \"1.20\"\n\n - name: \"Install TinyGo\"\n uses: rajatjindal/setup-actions/tinygo@v0.0.1\n with:\n version: v0.27.0\n\n - name: Install Spin\n uses: fermyon/actions/spin/setup@v1\n\n - name: Build and deploy\n uses: fermyon/actions/spin/deploy@v1\n with:\n fermyon_token: $\\{{ secrets.FERMYON_CLOUD_TOKEN }}{{ blockEnd }}{{ blockEnd }}Save the file.undefined","keywords":"","subsectionKeywords":"","url":"/cloud/github-actions.md#create-the-deployment-workflow"},{"project":"cloud","title":"Deploying Spin Apps Using GitHub Actions","subheading":"Push the Workflow to GitHub","content":"Now it's time to put our GitHub Action to the test. Run the following commands to push your local changes to the GitHub repository and trigger a Spin application deployment on Fermyon Cloud.$ git add .\n$ git commit -m \"Deployment workflow\"\n$ git pushYou should see a successful run on your GitHub Actions view on GitHub.undefinedYou should also see your Spin application in Fermyon Cloud.undefinedundefinedCongratulations on deploying your first Spin application using GitHub Actions!","keywords":"","subsectionKeywords":"","url":"/cloud/github-actions.md#push-the-workflow-to-github"},{"project":"cloud","title":"Deploying Spin Apps Using GitHub Actions","subheading":"Next Steps","content":"undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/github-actions.md#next-steps"},{"project":"cloud","title":"Fermyon Cloud","subheading":"","content":"undefinedundefinedundefinedundefined is a cloud application platform for WebAssembly-based serverless functions and microservices. It enables you to run undefined, at scale, in the cloud, without any infrastructure setup or maintenance required. You can take the same Spin applications you were running locally with spin up and push them to Fermyon Cloud with a simple spin deploy command. That's just the beginning; let's take a closer look at what you can learn about with the Fermyon Cloud documentation.","subsectionKeywords":"","url":"/cloud/index"},{"project":"cloud","title":"Fermyon Cloud","subheading":"Documentation","content":"This site contains documentation to help you succeed in deploying and managing resources on the Fermyon Cloud. Here you will:undefinedundefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/index.md#documentation"},{"project":"cloud","title":"Fermyon Cloud","subheading":"Getting Support","content":"If you have any questions (and cannot find answers) on this site, please go to our undefined for assistance.","keywords":"","subsectionKeywords":"","url":"/cloud/index.md#getting-support"},{"project":"cloud","title":"Fermyon Cloud","subheading":"Open Beta","content":"The Fermyon Cloud is currently an open beta service. This means that Fermyon Technologies does not provide any service-level agreements on the service, including the workloads you choose to deploy. We also reserve the right to break compatibility with:undefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/index.md#open-beta"},{"project":"cloud","title":"Let's get started","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedThis guide will get you up and running in the Fermyon Cloud in less than two minutes. To do so, we've already made a Spin application ready for you to deploy to the cloud.","subsectionKeywords":"","url":"/cloud/quickstart"},{"project":"cloud","title":"Let's get started","subheading":"Install Spin","content":"{{ tabs \"os\" }}{{ startTab \"Linux\"}}Download the spin binary using the install.sh script hosted on this site:Then move the spin binary somewhere in your path, so you can run it from anywhere. For example:$ sudo mv ./spin /usr/local/bin/spin{{ blockEnd }}{{ startTab \"macOS\"}}Download the spin binary using the install.sh script hosted on this site:Then move the spin binary somewhere in your path, so you can run it from anywhere. For example:$ sudo mv ./spin /usr/local/bin/spin{{ blockEnd }}{{ startTab \"Windows\"}}Download the Windows binary release of Spin from GitHub.Unzip the binary release and place the spin.exe in your system path.{{ blockEnd }}{{ blockEnd }}{{ details \"Learn more\" \"undefined is an open-source project used for creating, developing, building, running, and deploying Spin applications. It is both a CLI tool and a runtime, and provides SDKs for a variety of programming languages, including, but not limited to, Rust, TinyGo, and C#. \\n\\n The Spin project provides installers that are supported on Linux (amd64), macOS (amd64 and arm64), and Windows(amd64). \\n\\n The undefined documentation is a good starting place to learn more about using the framework to develop applications.\"}}","keywords":"","subsectionKeywords":"","url":"/cloud/quickstart.md#install-spin"},{"project":"cloud","title":"Let's get started","subheading":"Log in to the Fermyon Cloud","content":"Now, let's log in to the Fermyon Cloud. You will be using your undefined to sign in:$ spin login\n\nCopy your one-time code:\n\nXXXXXXXX\n\n...and open the authorization page in your browser:\n\nhttps://cloud.fermyon.com/device-authorization\n\nWaiting for device authorization...\nDevice authorized!This command will generate an authentication code for your current device to be authorized against the Fermyon Cloud. Follow the instructions in the prompt to complete the authorization process.{{ details \"Learn more\" \"The default behavior of spin login is to authenticate with the Fermyon Cloud. The command can authenticate against any instance of the undefined.\" }}","keywords":"","subsectionKeywords":"","url":"/cloud/quickstart.md#log-in-to-the-fermyon-cloud"},{"project":"cloud","title":"Let's get started","subheading":"Clone the Quickstart Repo","content":"To make this easy, we've already compiled a Webassembly module and created a Spin application for you to deploy.Let's go ahead and clone the undefined repository to your computer to retrieve that application:$ git clone https://github.com/fermyon/cloud-start && cd cloud-startThis command clones the repository into a local directory named cloud-start, and then enters that directory.{{ details \"Learn more\" \"To write your Spin application, you would start by using the command spin new [template name] [project name]. This gives you the option to select a template based on your preference of programming language and trigger for your module - e.g., spin new rust-http my_rust_http_trigger. \\n\\n The undefined guides you through creating a Spin application from scratch.\" }}","keywords":"","subsectionKeywords":"","url":"/cloud/quickstart.md#clone-the-quickstart-repo"},{"project":"cloud","title":"Let's get started","subheading":"Deploy the Application","content":"Let's deploy the application:$ spin deployThe spin command will run using the Spin binary in your system path and read the Spin application definition file spin.toml in the current (cloud-start) directory to know what application to deploy.{{ details \"Learn more\" \"Deploying a Spin application to the Fermyon Cloud includes packaging the application and all the required files, uploading it to a Bindle registry, as well as instantiating the application on the cloud. \\n\\n You can learn more about the deployment process undefined.\" }}","keywords":"","subsectionKeywords":"","url":"/cloud/quickstart.md#deploy-the-application"},{"project":"cloud","title":"Let's get started","subheading":"Success","content":"This is what a successful Spin application deployment on Fermyon Cloud looks like:Uploading cloud_start version 0.1.0+XXXXXXXX...\nDeploying...\nWaiting for application to become ready... ready\nAvailable Routes:\n cloud-start: https://cloud-start-XXXXXXXX.fermyon.app (wildcard)You can CTRL+Click on the link in the terminal to visit the web application you just deployed.Congratulations, you've now deployed your first Spin application to the undefined","keywords":"","subsectionKeywords":"","url":"/cloud/quickstart.md#success"},{"project":"cloud","title":"Let's get started","subheading":"Next Steps","content":"undefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/quickstart.md#next-steps"},{"project":"cloud","title":"Rest API","subheading":"","content":"undefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/cloud/rest-api"},{"project":"cloud","title":"Rest API","subheading":"OpenAPI","content":"The undefined project contains automatically generated client libraries for use with the Fermyon Cloud.","keywords":"","subsectionKeywords":"","url":"/cloud/rest-api.md#openapi"},{"project":"cloud","title":"Rest API","subheading":"Rust Client","content":"To use undefined, go ahead and add a reference to the Fermyon Cloud OpenAPI in your project’s Cargo.toml file, as shown below:cloud-openapi = { git = \"https://github.com/fermyon/cloud-openapi\" }","keywords":"","subsectionKeywords":"","url":"/cloud/rest-api.md#rust-client"},{"project":"cloud","title":"Rest API","subheading":"Documenting the Fermyon Cloud API","content":"The documentation for each client library is automatically generated and is available in the undefined.","keywords":"","subsectionKeywords":"","url":"/cloud/rest-api.md#documenting-the-fermyon-cloud-api"},{"project":"cloud","title":"Rest API","subheading":"Related Resources","content":"undefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/rest-api.md#related-resources"},{"project":"cloud","title":"Spin Visual Studio (VS) Code Extension","subheading":"","content":"undefinedundefinedundefined","subsectionKeywords":"","url":"/cloud/spin-vs-code-extension"},{"project":"cloud","title":"Spin Visual Studio (VS) Code Extension","subheading":"Visual Studio Code","content":"undefined is a source-code editor that can be used with a variety of programming languages. It was recently ranked the most popular developer environment tool (by 82, 000 respondents in the undefined).","keywords":"","subsectionKeywords":"","url":"/cloud/spin-vs-code-extension.md#visual-studio-code"},{"project":"cloud","title":"Spin Visual Studio (VS) Code Extension","subheading":"The Spin Extension","content":"VS Code extensions allow you to add tools to your installation, to support your development workflow. Fermyon's undefined integrates Spin developer tasks into the VS Code editor.","keywords":"","subsectionKeywords":"","url":"/cloud/spin-vs-code-extension.md#the-spin-extension"},{"project":"cloud","title":"Spin Visual Studio (VS) Code Extension","subheading":"Installation","content":"The Spin Extension for VS Code can be found in the undefined and can also be installed by searching for Fermyon Spin within the Extensions section of your VS Code editor. The extension downloads and uses a compatible version of Spin by default.","keywords":"","subsectionKeywords":"","url":"/cloud/spin-vs-code-extension.md#installation"},{"project":"cloud","title":"Spin Visual Studio (VS) Code Extension","subheading":"Supported Spin Commands","content":"At present, the Spin VS Code Extension supports the undefined, undefined and undefined commands. Simply open your VS Code terminal (from the main VS Code menu bar) to perform these commands.","keywords":"","subsectionKeywords":"","url":"/cloud/spin-vs-code-extension.md#supported-spin-commands"},{"project":"cloud","title":"Spin Visual Studio (VS) Code Extension","subheading":"Fermyon Cloud and VS Code","content":"undefined shows you how to create, build, deploy, update and re-deploy a new Spin application, all from within VS Code. The video is based on using Fermyon Cloud","keywords":"","subsectionKeywords":"","url":"/cloud/spin-vs-code-extension.md#fermyon-cloud-and-vs-code"},{"project":"cloud","title":"Support","subheading":"","content":"undefinedundefinedundefined","keywords":"abuse security concern","subsectionKeywords":"","url":"/cloud/support"},{"project":"cloud","title":"Support","subheading":"Engage on Discord","content":"As the Fermyon Cloud is currently in Beta, support is being provided best effort through undefined. This link will take you to the undefined channel, which is a good place to start.For support related to undefined, undefined, and other projects, please use the appropriate channels on Discord as well.","keywords":"","subsectionKeywords":"","url":"/cloud/support.md#engage-on-discord"},{"project":"cloud","title":"Support","subheading":"Report Issues on GitHub","content":"To see any know issues and report a specific issue, feel free to use the undefined","keywords":"","subsectionKeywords":"","url":"/cloud/support.md#report-issues-on-github"},{"project":"cloud","title":"Support","subheading":"Report Abuse","content":"To report suspected abuse of the Fermyon Cloud service, please e-mail abuse@fermyon.com.","keywords":"","subsectionKeywords":"","url":"/cloud/support.md#report-abuse"},{"project":"cloud","title":"Upgrade an Application","subheading":"","content":"undefinedundefinedTo upgrade your Spin application, there are just a few steps to take. This doesn’t require so much stress.","subsectionKeywords":"","url":"/cloud/upgrade"},{"project":"cloud","title":"Upgrade an Application","subheading":"Upgrade Your Application","content":"To upgrade a Spin application running in the Fermyon Cloud, you first have to change the version of your application.undefinedversion = \"0.1.0\"In this case, we're changing the version from 0.1.0 to 0.1.1:version = \"0.1.1\"{{ details \"Additional info\" \"Spin application are packaged using undefined. Bindle ensures immutability, meaning you cannot overwrite a Bindle (name+version), once it has been uploaded to a Bindle server. /n/n spin deploy will fail if you try to redeploy a version of an application, which already exists in the Fermyon Cloud.\" }}You can now deploy the upgraded version of your application by running this command:$ spin deployThat’s how to upgrade a Spin Application, just as simple as that!","keywords":"","subsectionKeywords":"","url":"/cloud/upgrade.md#upgrade-your-application"},{"project":"cloud","title":"Upgrade an Application","subheading":"Next Steps","content":"undefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/upgrade.md#next-steps"},{"project":"cloud","title":"The User Settings Screen","subheading":"","content":"undefinedundefined","subsectionKeywords":"","url":"/cloud/user-settings"},{"project":"cloud","title":"The User Settings Screen","subheading":"Fermyon Cloud Account Settings","content":"User settings empower you to take control of your Fermyon Cloud experience. Today, you can:undefinedundefinedSee the relevant sections below, for instructions on how to perform these actions.","keywords":"","subsectionKeywords":"","url":"/cloud/user-settings.md#fermyon-cloud-account-settings"},{"project":"cloud","title":"The User Settings Screen","subheading":"Create and Manage a Personal Access Token","content":"","keywords":"","subsectionKeywords":"","url":"/cloud/user-settings.md#create-and-manage-a-personal-access-token"},{"project":"cloud","title":"The User Settings Screen","subheading":"Create a PAT","content":"A Personal Access Tokens (PAT) enable continuous integration workflows by providing an alternative to the standard login procedure, which requires you to go through Fermyon Cloud to activate the device code.Examples of this include deploying your Spin applications to Fermyon Cloud using the undefined. Let's walk through the steps required to create a PAT.First, undefined Fermyon Cloud. Hover over your username in the top right corner and select 'User Settings' from the down options.undefinedNow select the \"Add a Token\" button under the Personal Access Tokens heading.undefinedEnter your preferred token name. We recommend that you use a descriptive name to help you can recall its purpose later.undefinedBe sure to, take the time to, copy your token name and store it in a safe location for later.undefinedExit out of the panel. Congratulations on creating your first PAT with Fermyon Cloud.undefined","keywords":"","subsectionKeywords":"","url":"/cloud/user-settings.md#create-a-pat"},{"project":"cloud","title":"The User Settings Screen","subheading":"Delete a PAT","content":"To delete a PAT, click on the x to the right of the desired token name. This will bring up the following prompt.undefinedType out the complete token name and hit delete to permanently delete your PAT. After performing the following step, the token will no longer work!undefinedYou should no longer see the PAT in your User Settings view.undefined","keywords":"","subsectionKeywords":"","url":"/cloud/user-settings.md#delete-a-pat"},{"project":"cloud","title":"The User Settings Screen","subheading":"Delete Your Fermyon Cloud Account","content":"You may wish to delete your Fermyon Cloud account. This is a permanent action that will delete your Fermyon account undefined associated with the account.First, undefined. Hover on your username in the top right corner and select 'User Settings' from the down options.undefinedClick the \"Delete account\" button in the undefined box.undefinedSelect all three checkboxes and select \"Delete account\" to proceed with account deletion.undefinedundefined","keywords":"","subsectionKeywords":"","url":"/cloud/user-settings.md#delete-your-fermyon-cloud-account"},{"project":"cloud","title":"The User Settings Screen","subheading":"Next Steps","content":"undefined","keywords":"","subsectionKeywords":"","url":"/cloud/user-settings.md#next-steps"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"","content":"undefined","subsectionKeywords":"","url":"/common/cli-reference"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Spin","content":"This page documents the Spin Command Line Interface (CLI). Specifically, all of the available Spin Options and Subcommands. For more information on command stability, see the undefined. You can reproduce the Spin CLI documentation on your machine by using the --help flag. For example:{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin --help\n\nUSAGE:\n spin \n\nOPTIONS:\n -h, --help Print help information\n -V, --version Print version information\n\nSUBCOMMANDS:\n add Scaffold a new component into an existing application\n build Build the Spin application\n cloud Commands for publishing applications to the Fermyon Platform\n deploy Package and upload an application to the Fermyon Platform\n help Print this message or the help of the given subcommand(s)\n login Log into the Fermyon Platform\n new Scaffold a new application based on a template\n plugins Install/uninstall Spin plugins\n registry Commands for working with OCI registries to distribute applications\n templates Commands for working with WebAssembly component templates\n up Start the Spin application{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin --help\n\nUSAGE:\n spin \n\nOPTIONS:\n -h, --help Print help information\n -V, --version Print version information\n\nSUBCOMMANDS:\n add Scaffold a new component into an existing application\n build Build the Spin application\n cloud Commands for publishing applications to the Fermyon Platform\n deploy Package and upload an application to the Fermyon Platform\n help Print this message or the help of the given subcommand(s)\n login Log into the Fermyon Platform\n new Scaffold a new application based on a template\n plugins Install/uninstall Spin plugins\n registry Commands for working with OCI registries to distribute applications\n templates Commands for working with WebAssembly component templates\n up Start the Spin application\n watch Rebuild and restart the Spin application when files changes{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#spin"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Add","content":"Adding a subcommand (and again issuing the --help command) will provide information specific to that particular subcommand. For example:{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin add --help\n\nspin-add \nScaffold a new component into an existing application\n\nUSAGE:\n spin add [OPTIONS] [ARGS]\n\nARGS:\n The template from which to create the new application or component. Run\n `spin templates list` to see available options\n The name of the new application or component\n\nOPTIONS:\n --accept-defaults An optional argument that allows to skip prompts for the\n manifest file by accepting the defaults if available on the\n template\n -f, --file Path to spin.toml\n -h, --help Print help information\n -o, --output The directory in which to create the new application or\n component. The default is the name argument\n --tag Filter templates to select by tags\n -v, --value Parameter values to be passed to the template (in name=value\n format)\n --values-file A TOML file which contains parameter values in name = \"value\"\n format. Parameters passed as CLI option overwrite parameters\n specified in the file{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin add --help\n\nspin-add \nScaffold a new component into an existing application\n\nUSAGE:\n spin add [OPTIONS] [ARGS]\n\nARGS:\n The template from which to create the new application or component. Run\n `spin templates list` to see available options\n The name of the new application or component\n\nOPTIONS:\n --accept-defaults An optional argument that allows to skip prompts for the\n manifest file by accepting the defaults if available on the\n template\n -f, --file Path to spin.toml\n -h, --help Print help information\n -o, --output The directory in which to create the new application or\n component. The default is the name argument\n --tag Filter templates to select by tags\n -v, --value Parameter values to be passed to the template (in name=value\n format)\n --values-file A TOML file which contains parameter values in name = \"value\"\n format. Parameters passed as CLI option overwrite parameters\n specified in the file{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#add"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Build","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin build --help\n\nspin-build \nBuild the Spin application\n\nUSAGE:\n spin build [OPTIONS] [UP_ARGS]...\n\nARGS:\n ... \n\nOPTIONS:\n -f, --from Path to application manifest. The default is \"spin.toml\"\n -h, --help Print help information\n -u, --up Run the application after building{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin build --help\n\nspin-build \nBuild the Spin application\n\nUSAGE:\n spin build [OPTIONS] [UP_ARGS]...\n\nARGS:\n ... \n\nOPTIONS:\n -f, --from Path to application manifest. The default is \"spin.toml\"\n -h, --help Print help information\n -u, --up Run the application after building{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#build"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Cloud","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin cloud --help\n\nspin-cloud \nCommands for publishing applications to the Fermyon Platform\n\nUSAGE:\n spin cloud \n\nOPTIONS:\n -h, --help Print help information\n\nSUBCOMMANDS:\n deploy Package and upload an application to the Fermyon Platform\n help Print this message or the help of the given subcommand(s)\n login Log into the Fermyon Platform{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin cloud --help\n\nspin-cloud \nCommands for publishing applications to the Fermyon Platform\n\nUSAGE:\n spin cloud \n\nOPTIONS:\n -h, --help Print help information\n\nSUBCOMMANDS:\n deploy Package and upload an application to the Fermyon Platform\n help Print this message or the help of the given subcommand(s)\n login Log into the Fermyon Platform{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#cloud"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Deploy (Cloud)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin cloud deploy --help\n\nspin-cloud-deploy \nPackage and upload an application to the Fermyon Platform\n\nUSAGE:\n spin cloud deploy [OPTIONS]\n\nOPTIONS:\n --buildinfo \n Build metadata to append to the bindle version\n\n -d, --staging-dir \n Path to assemble the bindle before pushing (defaults to a temporary directory)\n\n -e, --deploy-existing-bindle\n Deploy existing bindle if it already exists on bindle server\n\n --environment-name \n Deploy to the Fermyon instance saved under the specified name. If omitted, Spin deploys\n to the default unnamed instance [env: FERMYON_DEPLOYMENT_ENVIRONMENT=]\n\n -f, --file \n Path to spin.toml [default: spin.toml]\n\n -h, --help\n Print help information\n\n --key-value \n Pass a key/value (key=value) to all components of the application. Can be used multiple\n times\n\n --no-buildinfo\n Disable attaching buildinfo [env: SPIN_DEPLOY_NO_BUILDINFO=]\n\n --readiness-timeout \n How long in seconds to wait for a deployed HTTP application to become ready. The default\n is 60 seconds. Set it to 0 to skip waiting for readiness [default: 60]{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin cloud deploy --help\n\nspin-cloud-deploy \nPackage and upload an application to the Fermyon Platform\n\nUSAGE:\n spin cloud deploy [OPTIONS]\n\nOPTIONS:\n --buildinfo \n Build metadata to append to the bindle version\n\n -d, --staging-dir \n Path to assemble the bindle before pushing (defaults to a temporary directory)\n\n -e, --deploy-existing-bindle\n Deploy existing bindle if it already exists on bindle server\n\n --environment-name \n Deploy to the Fermyon instance saved under the specified name. If omitted, Spin deploys\n to the default unnamed instance [env: FERMYON_DEPLOYMENT_ENVIRONMENT=]\n\n -f, --file \n Path to spin.toml [default: spin.toml]\n\n -h, --help\n Print help information\n\n --key-value \n Pass a key/value (key=value) to all components of the application. Can be used multiple\n times\n\n --no-buildinfo\n Disable attaching buildinfo [env: SPIN_DEPLOY_NO_BUILDINFO=]\n\n --readiness-timeout \n How long in seconds to wait for a deployed HTTP application to become ready. The default\n is 60 seconds. Set it to 0 to skip waiting for readiness [default: 60]{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#deploy-cloud"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Login (Cloud)","content":"Please note: the previous spin login command (from versions before Spin v0.9.0) has been kept to ensure backward compatibility. In the Spin v0.9.0 release, both the spin login --help and spin cloud login --help commands will produce the same output, which is as follows:{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin cloud login --help\n\nspin-cloud-login \nLog into the Fermyon Platform\n\nUSAGE:\n spin cloud login [OPTIONS]\n\nOPTIONS:\n --auth-method \n [env: AUTH_METHOD=] [possible values: github, username, token]\n\n --bindle-password \n Basic http auth password for the bindle server [env: BINDLE_PASSWORD=]\n\n --bindle-server \n URL of bindle server [env: BINDLE_URL=]\n\n --bindle-username \n Basic http auth username for the bindle server [env: BINDLE_USERNAME=]\n\n --environment-name \n Save the login details under the specified name instead of making them the default. Use\n named environments with `spin deploy --environment-name ` [env:\n FERMYON_DEPLOYMENT_ENVIRONMENT=]\n\n -h, --help\n Print help information\n\n -k, --insecure\n Ignore server certificate errors from bindle and hippo\n\n --list\n List saved logins\n\n --password \n Hippo password [env: HIPPO_PASSWORD=]\n\n --status\n Display login status\n\n --token \n Auth Token [env: SPIN_AUTH_TOKEN=]\n\n --url \n URL of hippo server [env: HIPPO_URL=] [default: https://cloud.fermyon.com/]\n\n --username \n Hippo username [env: HIPPO_USERNAME=]{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin cloud login --help\n\nspin-cloud-login \nLog into the Fermyon Platform\n\nUSAGE:\n spin cloud login [OPTIONS]\n\nOPTIONS:\n --auth-method \n [env: AUTH_METHOD=] [possible values: github, username, token]\n\n --bindle-password \n Basic http auth password for the bindle server [env: BINDLE_PASSWORD=]\n\n --bindle-server \n URL of bindle server [env: BINDLE_URL=]\n\n --bindle-username \n Basic http auth username for the bindle server [env: BINDLE_USERNAME=]\n\n --environment-name \n Save the login details under the specified name instead of making them the default. Use\n named environments with `spin deploy --environment-name ` [env:\n FERMYON_DEPLOYMENT_ENVIRONMENT=]\n\n -h, --help\n Print help information\n\n -k, --insecure\n Ignore server certificate errors from bindle and hippo\n\n --list\n List saved logins\n\n --password \n Hippo password [env: HIPPO_PASSWORD=]\n\n --status\n Display login status\n\n --token \n Auth Token [env: SPIN_AUTH_TOKEN=]\n\n --url \n URL of hippo server [env: HIPPO_URL=] [default: https://cloud.fermyon.com/]\n\n --username \n Hippo username [env: HIPPO_USERNAME=]{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#login-cloud"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Deploy","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin deploy --help\n\nspin-deploy \nPackage and upload an application to the Fermyon Platform\n\nUSAGE:\n spin deploy [OPTIONS]\n\nOPTIONS:\n --buildinfo \n Build metadata to append to the bindle version\n\n -d, --staging-dir \n Path to assemble the bindle before pushing (defaults to a temporary directory)\n\n -e, --deploy-existing-bindle\n Deploy existing bindle if it already exists on bindle server\n\n --environment-name \n Deploy to the Fermyon instance saved under the specified name. If omitted, Spin deploys\n to the default unnamed instance [env: FERMYON_DEPLOYMENT_ENVIRONMENT=]\n\n -f, --file \n Path to spin.toml [default: spin.toml]\n\n -h, --help\n Print help information\n\n --key-value \n Pass a key/value (key=value) to all components of the application. Can be used multiple\n times\n\n --no-buildinfo\n Disable attaching buildinfo [env: SPIN_DEPLOY_NO_BUILDINFO=]\n\n --readiness-timeout \n How long in seconds to wait for a deployed HTTP application to become ready. The default\n is 60 seconds. Set it to 0 to skip waiting for readiness [default: 60]{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin deploy --help\n\nspin-deploy \nPackage and upload an application to the Fermyon Platform\n\nUSAGE:\n spin deploy [OPTIONS]\n\nOPTIONS:\n --buildinfo \n Build metadata to append to the bindle version\n\n -d, --staging-dir \n Path to assemble the bindle before pushing (defaults to a temporary directory)\n\n -e, --deploy-existing-bindle\n Deploy existing bindle if it already exists on bindle server\n\n --environment-name \n Deploy to the Fermyon instance saved under the specified name. If omitted, Spin deploys\n to the default unnamed instance [env: FERMYON_DEPLOYMENT_ENVIRONMENT=]\n\n -f, --file \n Path to spin.toml [default: spin.toml]\n\n -h, --help\n Print help information\n\n --key-value \n Pass a key/value (key=value) to all components of the application. Can be used multiple\n times\n\n --no-buildinfo\n Disable attaching buildinfo [env: SPIN_DEPLOY_NO_BUILDINFO=]\n\n --readiness-timeout \n How long in seconds to wait for a deployed HTTP application to become ready. The default\n is 60 seconds. Set it to 0 to skip waiting for readiness [default: 60]{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#deploy"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Help","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin help \n\nspin 1.0.0 \nThe Spin CLI\n\nOPTIONS:\n -h, --help Print help information\n -V, --version Print version information\n\nSUBCOMMANDS:\n add Scaffold a new component into an existing application\n build Build the Spin application\n cloud Commands for publishing applications to the Fermyon Platform\n deploy Package and upload an application to the Fermyon Platform\n help Print this message or the help of the given subcommand(s)\n login Log into the Fermyon Platform\n new Scaffold a new application based on a template\n plugins Install/uninstall Spin plugins\n registry Commands for working with OCI registries to distribute applications\n templates Commands for working with WebAssembly component templates\n up Start the Spin application{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin help \n\nspin 1.1.0 \nThe Spin CLI\n\nUSAGE:\n spin \n\nOPTIONS:\n -h, --help Print help information\n -V, --version Print version information\n\nSUBCOMMANDS:\n add Scaffold a new component into an existing application\n build Build the Spin application\n cloud Commands for publishing applications to the Fermyon Platform\n deploy Package and upload an application to the Fermyon Platform\n help Print this message or the help of the given subcommand(s)\n login Log into the Fermyon Platform\n new Scaffold a new application based on a template\n plugins Install/uninstall Spin plugins\n registry Commands for working with OCI registries to distribute applications\n templates Commands for working with WebAssembly component templates\n up Start the Spin application\n watch Rebuild and restart the Spin application when files changes{{ blockEnd }}{{ blockEnd }}undefined","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#help"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Login","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin login --help\n\nspin-login \nLog into the Fermyon Platform\n\nUSAGE:\n spin login [OPTIONS]\n\nOPTIONS:\n --auth-method \n [env: AUTH_METHOD=] [possible values: github, username, token]\n\n --bindle-password \n Basic http auth password for the bindle server [env: BINDLE_PASSWORD=]\n\n --bindle-server \n URL of bindle server [env: BINDLE_URL=]\n\n --bindle-username \n Basic http auth username for the bindle server [env: BINDLE_USERNAME=]\n\n --environment-name \n Save the login details under the specified name instead of making them the default. Use\n named environments with `spin deploy --environment-name ` [env:\n FERMYON_DEPLOYMENT_ENVIRONMENT=]\n\n -h, --help\n Print help information\n\n -k, --insecure\n Ignore server certificate errors from bindle and hippo\n\n --list\n List saved logins\n\n --password \n Hippo password [env: HIPPO_PASSWORD=]\n\n --status\n Display login status\n\n --token \n Auth Token [env: SPIN_AUTH_TOKEN=]\n\n --url \n URL of hippo server [env: HIPPO_URL=] [default: https://cloud.fermyon.com/]\n\n --username \n Hippo username [env: HIPPO_USERNAME=]{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin login --help\n\nspin-login \nLog into the Fermyon Platform\n\nUSAGE:\n spin login [OPTIONS]\n\nOPTIONS:\n --auth-method \n [env: AUTH_METHOD=] [possible values: github, username, token]\n\n --bindle-password \n Basic http auth password for the bindle server [env: BINDLE_PASSWORD=]\n\n --bindle-server \n URL of bindle server [env: BINDLE_URL=]\n\n --bindle-username \n Basic http auth username for the bindle server [env: BINDLE_USERNAME=]\n\n --environment-name \n Save the login details under the specified name instead of making them the default. Use\n named environments with `spin deploy --environment-name ` [env:\n FERMYON_DEPLOYMENT_ENVIRONMENT=]\n\n -h, --help\n Print help information\n\n -k, --insecure\n Ignore server certificate errors from bindle and hippo\n\n --list\n List saved logins\n\n --password \n Hippo password [env: HIPPO_PASSWORD=]\n\n --status\n Display login status\n\n --token \n Auth Token [env: SPIN_AUTH_TOKEN=]\n\n --url \n URL of hippo server [env: HIPPO_URL=] [default: https://cloud.fermyon.com/]\n\n --username \n Hippo username [env: HIPPO_USERNAME=]{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#login"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"New","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin new --help \n\nspin-new \nScaffold a new application based on a template\n\nUSAGE:\n spin new [OPTIONS] [ARGS]\n\nARGS:\n The template from which to create the new application or component. Run\n `spin templates list` to see available options\n The name of the new application or component\n\nOPTIONS:\n --accept-defaults An optional argument that allows to skip prompts for the\n manifest file by accepting the defaults if available on the\n template\n -h, --help Print help information\n -o, --output The directory in which to create the new application or\n component. The default is the name argument\n --tag Filter templates to select by tags\n -v, --value Parameter values to be passed to the template (in name=value\n format)\n --values-file A TOML file which contains parameter values in name = \"value\"\n format. Parameters passed as CLI option overwrite parameters\n specified in the file{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin new --help \n\nspin-new \nScaffold a new application based on a template\n\nUSAGE:\n spin new [OPTIONS] [ARGS]\n\nARGS:\n The template from which to create the new application or component. Run\n `spin templates list` to see available options\n The name of the new application or component\n\nOPTIONS:\n --accept-defaults An optional argument that allows to skip prompts for the\n manifest file by accepting the defaults if available on the\n template\n -h, --help Print help information\n -o, --output The directory in which to create the new application or\n component. The default is the name argument\n --tag Filter templates to select by tags\n -v, --value Parameter values to be passed to the template (in name=value\n format)\n --values-file A TOML file which contains parameter values in name = \"value\"\n format. Parameters passed as CLI option overwrite parameters\n specified in the file{{ blockEnd }}{{ blockEnd }}undefined. These commands are similar except that:undefinedundefined","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#new"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Plugins","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin plugins --help\n\nspin-plugins \nInstall/uninstall Spin plugins\n\nUSAGE:\n spin plugins \n\nOPTIONS:\n -h, --help Print help information\n\nSUBCOMMANDS:\n help Print this message or the help of the given subcommand(s)\n install Install plugin from a manifest\n list List available or installed plugins\n uninstall Remove a plugin from your installation\n update Fetch the latest Spin plugins from the spin-plugins repository\n upgrade Upgrade one or all plugins{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin plugins --help\n\nspin-plugins \nInstall/uninstall Spin plugins\n\nUSAGE:\n spin plugins \n\nOPTIONS:\n -h, --help Print help information\n\nSUBCOMMANDS:\n help Print this message or the help of the given subcommand(s)\n install Install plugin from a manifest\n list List available or installed plugins\n uninstall Remove a plugin from your installation\n update Fetch the latest Spin plugins from the spin-plugins repository\n upgrade Upgrade one or all plugins{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#plugins"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Install (Plugins)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin plugins install --help\n\nspin-plugins-install \nInstall plugin from a manifest.\n\nThe binary file and manifest of the plugin is copied to the local Spin plugins directory.\n\nUSAGE:\n spin plugins install [OPTIONS] [PLUGIN_NAME]\n\nARGS:\n \n Name of Spin plugin\n\nOPTIONS:\n -f, --file \n Path to local plugin manifest\n\n -h, --help\n Print help information\n\n --override-compatibility-check\n Overrides a failed compatibility check of the plugin with the current version of Spin\n\n -u, --url \n URL of remote plugin manifest to install\n\n -v, --version \n Specific version of a plugin to be install from the centralized plugins repository\n\n -y, --yes\n Skips prompt to accept the installation of the plugin{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin plugins install --help\n\nspin-plugins-install \nInstall plugin from a manifest.\n\nThe binary file and manifest of the plugin is copied to the local Spin plugins directory.\n\nUSAGE:\n spin plugins install [OPTIONS] [PLUGIN_NAME]\n\nARGS:\n \n Name of Spin plugin\n\nOPTIONS:\n -f, --file \n Path to local plugin manifest\n\n -h, --help\n Print help information\n\n --override-compatibility-check\n Overrides a failed compatibility check of the plugin with the current version of Spin\n\n -u, --url \n URL of remote plugin manifest to install\n\n -v, --version \n Specific version of a plugin to be install from the centralized plugins repository\n\n -y, --yes\n Skips prompt to accept the installation of the plugin{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#install-plugins"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"List (Plugins)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin plugins list --help \n\nspin-plugins-list \nList available or installed plugins\n\nUSAGE:\n spin plugins list [OPTIONS]\n\nOPTIONS:\n -h, --help Print help information\n --installed List only installed plugins{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin plugins list --help \n\nspin-plugins-list \nList available or installed plugins\n\nUSAGE:\n spin plugins list [OPTIONS]\n\nOPTIONS:\n -h, --help Print help information\n --installed List only installed plugins{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#list-plugins"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Uninstall (Plugins)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin plugins uninstall --help\n\nspin-plugins-uninstall \nRemove a plugin from your installation\n\nUSAGE:\n spin plugins uninstall \n\nARGS:\n Name of Spin plugin\n\nOPTIONS:\n -h, --help Print help information{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin plugins uninstall --help\n\nspin-plugins-uninstall \nRemove a plugin from your installation\n\nUSAGE:\n spin plugins uninstall \n\nARGS:\n Name of Spin plugin\n\nOPTIONS:\n -h, --help Print help information{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#uninstall-plugins"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Update (Plugins)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin plugins update --help \n\nspin-plugins-update \nFetch the latest Spin plugins from the spin-plugins repository\n\nUSAGE:\n spin plugins update\n\nOPTIONS:\n -h, --help Print help information{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin plugins update --help \n\nspin-plugins-update \nFetch the latest Spin plugins from the spin-plugins repository\n\nUSAGE:\n spin plugins update\n\nOPTIONS:\n -h, --help Print help information{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#update-plugins"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Upgrade (Plugins)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin plugins upgrade --help\n\nspin-plugins-upgrade \nUpgrade one or all plugins\n\nUSAGE:\n spin plugins upgrade [OPTIONS] [PLUGIN_NAME]\n\nARGS:\n Name of Spin plugin to upgrade\n\nOPTIONS:\n -a, --all\n Upgrade all plugins\n\n -d, --downgrade\n Allow downgrading a plugin's version\n\n -f, --file \n Path to local plugin manifest\n\n -h, --help\n Print help information\n\n --override-compatibility-check\n Overrides a failed compatibility check of the plugin with the current version of Spin\n\n -u, --url \n Path to remote plugin manifest\n\n -v, --version \n Specific version of a plugin to be install from the centralized plugins repository\n\n -y, --yes\n Skips prompt to accept the installation of the plugin[s]{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin plugins upgrade --help\n\nspin-plugins-upgrade \nUpgrade one or all plugins\n\nUSAGE:\n spin plugins upgrade [OPTIONS] [PLUGIN_NAME]\n\nARGS:\n Name of Spin plugin to upgrade\n\nOPTIONS:\n -a, --all\n Upgrade all plugins\n\n -d, --downgrade\n Allow downgrading a plugin's version\n\n -f, --file \n Path to local plugin manifest\n\n -h, --help\n Print help information\n\n --override-compatibility-check\n Overrides a failed compatibility check of the plugin with the current version of Spin\n\n -u, --url \n Path to remote plugin manifest\n\n -v, --version \n Specific version of a plugin to be install from the centralized plugins repository\n\n -y, --yes\n Skips prompt to accept the installation of the plugin[s]{{ blockEnd }}{{ blockEnd }}undefined For additional information, please see the undefined and/or undefined sections of the documentation.","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#upgrade-plugins"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"OCI Registry","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin registry --help\n\nspin-registry \nCommands for working with OCI registries to distribute applications. Currently, the OCI commands are reusing the\ncredentials from ~/.docker/config.json to authenticate to registries\n\nUSAGE:\n spin registry \n\nOPTIONS:\n -h, --help Print help information\n\nSUBCOMMANDS:\n help Print this message or the help of the given subcommand(s)\n login Log in to a registry\n pull Pull a Spin application from a registry\n push Push a Spin application to a registry{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin registry --help\n\nspin-registry \nCommands for working with OCI registries to distribute applications. Currently, the OCI commands are reusing the\ncredentials from ~/.docker/config.json to authenticate to registries\n\nUSAGE:\n spin registry \n\nOPTIONS:\n -h, --help Print help information\n\nSUBCOMMANDS:\n help Print this message or the help of the given subcommand(s)\n login Log in to a registry\n pull Pull a Spin application from a registry\n push Push a Spin application to a registry{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#oci-registry"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Login (OCI Registry)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin registry login --help\n\nspin-registry-login \nLog in to a registry\n\nUSAGE:\n spin registry login [OPTIONS] \n\nARGS:\n \n\nOPTIONS:\n -h, --help Print help information\n -p, --password Password for the registry\n --password-stdin Take the password from stdin\n -u, --username Username for the registry{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin registry login --help\n\nspin-registry-login \nLog in to a registry\n\nUSAGE:\n spin registry login [OPTIONS] \n\nARGS:\n \n\nOPTIONS:\n -h, --help Print help information\n -p, --password Password for the registry\n --password-stdin Take the password from stdin\n -u, --username Username for the registry{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#login-oci-registry"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Pull (OCI Registry)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin registry pull --help\n\nspin-registry-pull \nPull a Spin application from a registry\n\nUSAGE:\n spin registry pull [OPTIONS] \n\nARGS:\n Reference of the Spin application\n\nOPTIONS:\n -h, --help Print help information\n -k, --insecure Ignore server certificate errors{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin registry pull --help\n\nspin-registry-pull \nPull a Spin application from a registry\n\nUSAGE:\n spin registry pull [OPTIONS] \n\nARGS:\n Reference of the Spin application\n\nOPTIONS:\n -h, --help Print help information\n -k, --insecure Ignore server certificate errors{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#pull-oci-registry"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Push (OCI Registry)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin registry push --help \n\nspin-registry-push \nPush a Spin application to a registry\n\nUSAGE:\n spin registry push [OPTIONS] \n\nARGS:\n Reference of the Spin application\n\nOPTIONS:\n -f, --file Path to spin.toml\n -h, --help Print help information\n -k, --insecure Ignore server certificate errors{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin registry push --help \n\nspin-registry-push \nPush a Spin application to a registry\n\nUSAGE:\n spin registry push [OPTIONS] \n\nARGS:\n Reference of the Spin application\n\nOPTIONS:\n -f, --file Path to spin.toml\n -h, --help Print help information\n -k, --insecure Ignore server certificate errors{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#push-oci-registry"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Templates","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin templates --help \n\nspin-templates \nCommands for working with WebAssembly component templates\n\nUSAGE:\n spin templates \n\nOPTIONS:\n -h, --help Print help information\n\nSUBCOMMANDS:\n help Print this message or the help of the given subcommand(s)\n install Install templates from a Git repository or local directory\n list List the installed templates\n uninstall Remove a template from your installation\n upgrade Upgrade templates to match your current version of Spin{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin templates --help \n\nspin-templates \nCommands for working with WebAssembly component templates\n\nUSAGE:\n spin templates \n\nOPTIONS:\n -h, --help Print help information\n\nSUBCOMMANDS:\n help Print this message or the help of the given subcommand(s)\n install Install templates from a Git repository or local directory\n list List the installed templates\n uninstall Remove a template from your installation\n upgrade Upgrade templates to match your current version of Spin{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#templates"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Install (Templates)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin templates install --help\n\nspin-templates-install \nInstall templates from a Git repository or local directory.\n\nThe files of the templates are copied to the local template store: a directory in your data or home\ndirectory.\n\nUSAGE:\n spin templates install [OPTIONS]\n\nOPTIONS:\n --branch \n The optional branch of the git repository\n\n --dir \n Local directory containing the template(s) to install\n\n --git \n The URL of the templates git repository. The templates must be in a git repository in a\n \"templates\" directory\n\n -h, --help\n Print help information\n\n --upgrade\n If present, updates existing templates instead of skipping{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin templates install --help\n\nspin-templates-install \nInstall templates from a Git repository or local directory.\n\nThe files of the templates are copied to the local template store: a directory in your data or home\ndirectory.\n\nUSAGE:\n spin templates install [OPTIONS]\n\nOPTIONS:\n --branch \n The optional branch of the git repository\n\n --dir \n Local directory containing the template(s) to install\n\n --git \n The URL of the templates git repository. The templates must be in a git repository in a\n \"templates\" directory\n\n -h, --help\n Print help information\n\n --upgrade\n If present, updates existing templates instead of skipping{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#install-templates"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"List (Templates)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin templates list --help \n\nspin-templates-list \nList the installed templates\n\nUSAGE:\n spin templates list [OPTIONS]\n\nOPTIONS:\n -h, --help Print help information\n --tag Filter templates matching all provided tags\n --verbose Whether to show additional template details in the list{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin templates list --help \n\nspin-templates-list \nList the installed templates\n\nUSAGE:\n spin templates list [OPTIONS]\n\nOPTIONS:\n -h, --help Print help information\n --tag Filter templates matching all provided tags\n --verbose Whether to show additional template details in the list{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#list-templates"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Uninstall (Templates)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin templates uninstall --help\n\nspin-templates-uninstall \nRemove a template from your installation\n\nUSAGE:\n spin templates uninstall \n\nARGS:\n The template to uninstall\n\nOPTIONS:\n -h, --help Print help information{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin templates uninstall --help\n\nspin-templates-uninstall \nRemove a template from your installation\n\nUSAGE:\n spin templates uninstall \n\nARGS:\n The template to uninstall\n\nOPTIONS:\n -h, --help Print help information{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#uninstall-templates"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Upgrade (Templates)","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin templates upgrade --help \n\nspin-templates-upgrade \nUpgrade templates to match your current version of Spin.\n\nThe files of the templates are copied to the local template store: a directory in your data or home\ndirectory.\n\nUSAGE:\n spin templates upgrade [OPTIONS]\n\nOPTIONS:\n --all\n By default, Spin displays the list of installed repositories and prompts you to choose\n which to upgrade. Pass this flag to upgrade all repositories without prompting\n\n --branch \n The optional branch of the git repository, if a specific repository is given\n\n -h, --help\n Print help information\n\n --repo \n By default, Spin displays the list of installed repositories and prompts you to choose\n which to upgrade. Pass this flag to upgrade only the specified repository without\n prompting{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin templates upgrade --help \n\nspin-templates-upgrade \nUpgrade templates to match your current version of Spin.\n\nThe files of the templates are copied to the local template store: a directory in your data or home\ndirectory.\n\nUSAGE:\n spin templates upgrade [OPTIONS]\n\nOPTIONS:\n --all\n By default, Spin displays the list of installed repositories and prompts you to choose\n which to upgrade. Pass this flag to upgrade all repositories without prompting\n\n --branch \n The optional branch of the git repository, if a specific repository is given\n\n -h, --help\n Print help information\n\n --repo \n By default, Spin displays the list of installed repositories and prompts you to choose\n which to upgrade. Pass this flag to upgrade only the specified repository without\n prompting{{ blockEnd }}{{ blockEnd }}undefined For additional information, please see the undefined and/or undefined sections of the documentation.","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#upgrade-templates"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Up","content":"The following options are available in relation to running your Spin application. Additionally, depending on the type of trigger that your application uses (i.e. HTTP or Redis trigger), there are trigger-specific options available. Details of the trigger options can be found in the next section (below).{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin up --help\n\nspin-up \nStart the Spin application\nUSAGE:\n spin up [OPTIONS]\n\nOPTIONS:\n --direct-mounts For local apps with directory mounts and no excluded files, mount\n them directly instead of using a temporary directory\n -e, --env Pass an environment variable (key=value) to all components of the\n application\n -f, --from The application to run. This may be a manifest (spin.toml) file, a\n directory containing a spin.toml file, or a remote registry\n reference. If omitted, it defaults to \"spin.toml\"\n -h, --help \n -k, --insecure Ignore server certificate errors from bindle server or registry\n --temp Temporary directory for the static assets of the components\n\nTRIGGER OPTIONS:\n --allow-transient-write\n Set the static assets of the components in the temporary directory as writable\n\n --cache \n Wasmtime cache configuration file\n \n [env: WASMTIME_CACHE_FILE=]\n\n --disable-cache\n Disable Wasmtime cache\n \n [env: DISABLE_WASMTIME_CACHE=]\n\n --follow \n Print output to stdout/stderr only for given component(s)\n\n -L, --log-dir \n Log directory for the stdout and stderr of components\n\n -q, --quiet\n Silence all component output to stdout/stderr\n\n --runtime-config-file \n Configuration file for config providers and wasmtime config\n \n [env: RUNTIME_CONFIG_FILE=]\n{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin up --help\n\nspin-up \nStart the Spin application\nUSAGE:\n spin up [OPTIONS]\n\nOPTIONS:\n --direct-mounts For local apps with directory mounts and no excluded files, mount\n them directly instead of using a temporary directory\n -e, --env Pass an environment variable (key=value) to all components of the\n application\n -f, --from The application to run. This may be a manifest (spin.toml) file, a\n directory containing a spin.toml file, or a remote registry\n reference. If omitted, it defaults to \"spin.toml\"\n -h, --help \n -k, --insecure Ignore server certificate errors from bindle server or registry\n --temp Temporary directory for the static assets of the components\n\nTRIGGER OPTIONS:\n --allow-transient-write\n Set the static assets of the components in the temporary directory as writable\n\n --cache \n Wasmtime cache configuration file\n \n [env: WASMTIME_CACHE_FILE=]\n\n --disable-cache\n Disable Wasmtime cache\n \n [env: DISABLE_WASMTIME_CACHE=]\n\n --follow \n Print output to stdout/stderr only for given component(s)\n\n -L, --log-dir \n Log directory for the stdout and stderr of components\n\n -q, --quiet\n Silence all component output to stdout/stderr\n\n --runtime-config-file \n Configuration file for config providers and wasmtime config\n \n [env: RUNTIME_CONFIG_FILE=]\n{{ blockEnd }}{{ blockEnd }}undefined","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#up"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Trigger Options","content":"","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#trigger-options"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Redis Request Handler","content":"Below, please see the available trigger options for the Redis request handler.{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin up --help\n\nspin-up \nStart the Spin application\n\nOPTIONS:\n --direct-mounts For local apps with directory mounts and no excluded files, mount\n them directly instead of using a temporary directory\n -e, --env Pass an environment variable (key=value) to all components of the\n application\n -f, --from The application to run. This may be a manifest (spin.toml) file, a\n directory containing a spin.toml file, or a remote registry\n reference. If omitted, it defaults to \"spin.toml\"\n -h, --help \n -k, --insecure Ignore server certificate errors from bindle server or registry\n --temp Temporary directory for the static assets of the components\n\nTRIGGER OPTIONS:\n --allow-transient-write\n Set the static assets of the components in the temporary directory as writable\n\n --cache \n Wasmtime cache configuration file\n \n [env: WASMTIME_CACHE_FILE=]\n\n --disable-cache\n Disable Wasmtime cache\n \n [env: DISABLE_WASMTIME_CACHE=]\n\n --follow \n Print output to stdout/stderr only for given component(s)\n\n -L, --log-dir \n Log directory for the stdout and stderr of components\n\n -q, --quiet\n Silence all component output to stdout/stderr\n\n --runtime-config-file \n Configuration file for config providers and wasmtime config\n \n [env: RUNTIME_CONFIG_FILE=]\n\n --state-dir \n Set the application state directory path. This is used in the default locations for\n logs, key value stores, etc.\n \n For local apps, this defaults to `.spin/` relative to the `spin.toml` file. For remote\n apps, this has no default (unset). Passing an empty value forces the value to be unset.{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin up --help\n\nspin-up \nStart the Spin application\n\nOPTIONS:\n --direct-mounts For local apps with directory mounts and no excluded files, mount\n them directly instead of using a temporary directory\n -e, --env Pass an environment variable (key=value) to all components of the\n application\n -f, --from The application to run. This may be a manifest (spin.toml) file, a\n directory containing a spin.toml file, or a remote registry\n reference. If omitted, it defaults to \"spin.toml\"\n -h, --help \n -k, --insecure Ignore server certificate errors from bindle server or registry\n --temp Temporary directory for the static assets of the components\n\nTRIGGER OPTIONS:\n --allow-transient-write\n Set the static assets of the components in the temporary directory as writable\n\n --cache \n Wasmtime cache configuration file\n \n [env: WASMTIME_CACHE_FILE=]\n\n --disable-cache\n Disable Wasmtime cache\n \n [env: DISABLE_WASMTIME_CACHE=]\n\n --follow \n Print output to stdout/stderr only for given component(s)\n\n -L, --log-dir \n Log directory for the stdout and stderr of components\n\n -q, --quiet\n Silence all component output to stdout/stderr\n\n --runtime-config-file \n Configuration file for config providers and wasmtime config\n \n [env: RUNTIME_CONFIG_FILE=]\n\n --state-dir \n Set the application state directory path. This is used in the default locations for\n logs, key value stores, etc.\n \n For local apps, this defaults to `.spin/` relative to the `spin.toml` file. For remote\n apps, this has no default (unset). Passing an empty value forces the value to be unset.{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#redis-request-handler"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"HTTP Request Handler","content":"Below, please see the available trigger options for the HTTP request handler. Note the additional three trigger options that the HTTP request handler offers (--listen, --tls-cert and --tls-key).{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}$ spin up --help\n\nspin-up \nStart the Spin application\n\nOPTIONS:\n --direct-mounts For local apps with directory mounts and no excluded files, mount\n them directly instead of using a temporary directory\n -e, --env Pass an environment variable (key=value) to all components of the\n application\n -f, --from The application to run. This may be a manifest (spin.toml) file, a\n directory containing a spin.toml file, or a remote registry\n reference. If omitted, it defaults to \"spin.toml\"\n -h, --help \n -k, --insecure Ignore server certificate errors from bindle server or registry\n --temp Temporary directory for the static assets of the components\n\nTRIGGER OPTIONS:\n --allow-transient-write\n Set the static assets of the components in the temporary directory as writable\n\n --cache \n Wasmtime cache configuration file\n \n [env: WASMTIME_CACHE_FILE=]\n\n --disable-cache\n Disable Wasmtime cache\n \n [env: DISABLE_WASMTIME_CACHE=]\n\n --follow \n Print output to stdout/stderr only for given component(s)\n\n -L, --log-dir \n Log directory for the stdout and stderr of components\n\n --listen
\n IP address and port to listen on\n \n [default: 127.0.0.1:3000]\n\n -q, --quiet\n Silence all component output to stdout/stderr\n\n --runtime-config-file \n Configuration file for config providers and wasmtime config\n \n [env: RUNTIME_CONFIG_FILE=]\n\n --state-dir \n Set the application state directory path. This is used in the default locations for\n logs, key value stores, etc.\n \n For local apps, this defaults to `.spin/` relative to the `spin.toml` file. For remote\n apps, this has no default (unset). Passing an empty value forces the value to be unset.\n\n --tls-cert \n The path to the certificate to use for https, if this is not set, normal http will be\n used. The cert should be in PEM format\n \n [env: SPIN_TLS_CERT=]\n\n --tls-key \n The path to the certificate key to use for https, if this is not set, normal http will\n be used. The key should be in PKCS#8 format\n \n [env: SPIN_TLS_KEY=]{{ blockEnd }}{{ startTab \"v1.1.0\"}}$ spin up --help\n\nspin-up \nStart the Spin application\n\nOPTIONS:\n --direct-mounts For local apps with directory mounts and no excluded files, mount\n them directly instead of using a temporary directory\n -e, --env Pass an environment variable (key=value) to all components of the\n application\n -f, --from The application to run. This may be a manifest (spin.toml) file, a\n directory containing a spin.toml file, or a remote registry\n reference. If omitted, it defaults to \"spin.toml\"\n -h, --help \n -k, --insecure Ignore server certificate errors from bindle server or registry\n --temp Temporary directory for the static assets of the components\n\nTRIGGER OPTIONS:\n --allow-transient-write\n Set the static assets of the components in the temporary directory as writable\n\n --cache \n Wasmtime cache configuration file\n \n [env: WASMTIME_CACHE_FILE=]\n\n --disable-cache\n Disable Wasmtime cache\n \n [env: DISABLE_WASMTIME_CACHE=]\n\n --follow \n Print output to stdout/stderr only for given component(s)\n\n -L, --log-dir \n Log directory for the stdout and stderr of components\n\n --listen
\n IP address and port to listen on\n \n [default: 127.0.0.1:3000]\n\n -q, --quiet\n Silence all component output to stdout/stderr\n\n --runtime-config-file \n Configuration file for config providers and wasmtime config\n \n [env: RUNTIME_CONFIG_FILE=]\n\n --state-dir \n Set the application state directory path. This is used in the default locations for\n logs, key value stores, etc.\n \n For local apps, this defaults to `.spin/` relative to the `spin.toml` file. For remote\n apps, this has no default (unset). Passing an empty value forces the value to be unset.\n\n --tls-cert \n The path to the certificate to use for https, if this is not set, normal http will be\n used. The cert should be in PEM format\n \n [env: SPIN_TLS_CERT=]\n\n --tls-key \n The path to the certificate key to use for https, if this is not set, normal http will\n be used. The key should be in PKCS#8 format\n \n [env: SPIN_TLS_KEY=]{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#http-request-handler"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"Watch","content":"{{ tabs \"spin-version\" }}{{ startTab \"v1.1.0\"}}$ spin watch --help\n\nspin-watch\nBuild and run the Spin application, rebuilding and restarting it when files change\n\nUSAGE:\n spin watch [OPTIONS] [UP_ARGS]...\n\nARGS:\n ... Arguments to be passed through to spin up\n\nOPTIONS:\n -c, --clear Clear the screen before each run\n -d, --debounce Set the timeout between detected change and re-execution, in\n milliseconds [default: 100]\n -f, --file Path to spin.toml [default: spin.toml]\n -h, --help Print help information\n --skip-build Only run the Spin application, restarting it when build\n artifacts change{{ blockEnd }}{{ blockEnd }}spin watch relies on configuration in your application manifest to know what files it should watch. For each component you should set the component.build.watch parameter with a list of glob patterns that your source files will match:[component.build]\n# Example watch configuration for a Rust application\nwatch = [\"src/**/*.rs\", \"Cargo.toml\"]The table below outlines exactly which files spin watch will monitor for changes depending on how you run the command. spin watch uses the configuration found on every component in your application.| Files | spin watch monitors for changes | spin watch --skip-build monitors for changes |\n| ----------------------- | ---------------------------------------------- | ---------------------------------------------- |\n| Application manifest | Yes | Yes |\n| component.build.watch | Yes | No |\n| component.files | Yes | Yes |\n| component.source | No (Yes if the component has no build command) | Yes |","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#watch"},{"project":"common","title":"Spin Command Line Interface (CLI) Reference","subheading":"CLI Stability Table","content":"CLI commands have four phases that indicate levels of stability:undefinedundefinedundefinedundefined{{ tabs \"spin-version\" }}{{ startTab \"v1.0.0\"}}| Command | Stability |\n| ---------------------------------------------------------- | ----------- |\n| spin add | Stable |\n| spin build | Stable |\n| spin new | Stable |\n| spin plugins | Stable |\n| spin templates | Stable |\n| spin up | Stable |\n| spin cloud | Stabilizing |\n| spin registry | Stabilizing |\n| spin bindle | Deprecated |{{ blockEnd }}{{ startTab \"v1.1.0\"}}| Command | Stability |\n| ------------------------------------------------------------------------------------- | ------------ |\n| spin add | Stable |\n| spin build | Stable |\n| spin new | Stable |\n| spin plugins | Stable |\n| spin templates | Stable |\n| spin up | Stable |\n| spin cloud | Stabilizing |\n| spin registry | Stabilizing |\n| spin watch | Experimental |\n| spin bindle | Deprecated |{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/common/cli-reference.md#cli-stability-table"},{"project":"common","title":"Contributing to Docs","subheading":"","content":"undefinedundefinedundefinedundefinedWe are delighted that you are interested in making our developer documentation better. Thank you! We welcome and appreciate contributions of all types — opening issues, fixing typos, adding examples, one-liner code fixes, tests, or complete features.Any contribution and interaction on any Fermyon project MUST follow our undefined. Thank you for being part of an inclusive and open community!Below are a few pointers designed to help you contribute.","keywords":"contribute contributing","subsectionKeywords":"","url":"/common/contributing-docs"},{"project":"common","title":"Contributing to Docs","subheading":"Technical Documentation Types","content":"The following points will help guide your contribution from a resource-type perspective; essentially we would really appreciate you creating and contributing any of the following 4 resource types.","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#technical-documentation-types"},{"project":"common","title":"Contributing to Docs","subheading":"1. Tutorials","content":"Tutorials are oriented toward learning. Tutorials are designed to get a user started on something new (that they have not tried before). You can think of a tutorial as a lesson i.e. teaching a Spin user undefined. The tutorial may contain many logically ordered steps i.e. installing Spin, installing Redis, using Spin templates, configuring a Spin application and so forth. The desired outcome for a tutorial is for the user to have a working deployment or application. Think of it as a lesson in how to bake a cake.","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#1-tutorials"},{"project":"common","title":"Contributing to Docs","subheading":"2. How-To Guides","content":"How-to guides are oriented towards showing a user how to solve a problem, which leads them to be able to achieve their own goal. The how-to guide will follow a series of logical steps. Think of it as providing a recipe for the user's creativity. For example, you can show a user how to undefined without telling them what the application must do; that is up to the user's imagination.","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#2-how-to-guides"},{"project":"common","title":"Contributing to Docs","subheading":"3. Reference","content":"Reference resources are merely a dry description; describing the feature in its simplest form. A great example of a reference resource is the undefined. You will notice that the CLI Reference page simply lists all of the commands and available options.","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#3-reference"},{"project":"common","title":"Contributing to Docs","subheading":"4. Explanation","content":"An explanation resource is written using a deep-dive approach i.e. providing a deep explanation with the view to impart a deep understanding of a particular concept, feature or product. You may find your contribution is so in-depth that it becomes a long form article like a blog post. If that is the case, please get in touch and we can discuss options around getting your content published on another platform; like the undefined.undefinedYou will notice that the menu system is organized in terms of \"Tutorial\", \"How-To\", \"Reference\" and so forth. When you write your contribution please decide which product (Cloud, Spin, Bartholomew) category it falls into and also which resource type it aligns with. Armed with that information you can go ahead and create your new file. For example, your \"how-to\" resource on \"developing a Spin application\" in Fermyon cloud would be saved to the content/cloud/ folder; specifically, content/cloud/develop.md and the menu item (for the left-hand-side menu) would be added to the templates/cloud_sidebar.hbs file, as shown below.undefinedThe resulting output would be as follows.undefined","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#4-explanation"},{"project":"common","title":"Contributing to Docs","subheading":"Documents Relevant to Two or More Projects","content":"If a document is relevant to two or more projects it is advised to place it in the new undefined folder area (i.e. as opposed to just placing it in the undefined folder or just placing it in undefined folder). Items in the common area can still be linked to from any of the menu templates i.e. undefined, undefined and undefined .hbs templates all link to this how-to-contribute document; which you are currently reading.","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#documents-relevant-to-two-or-more-projects"},{"project":"common","title":"Contributing to Docs","subheading":"Technical Documentation Procedure (Video)","content":"","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#technical-documentation-procedure-video"},{"project":"common","title":"Contributing to Docs","subheading":"Technical Documentation Procedure (Text)","content":"","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#technical-documentation-procedure-text"},{"project":"common","title":"Contributing to Docs","subheading":"1. Fork the Repository","content":"The first step is to fork the undefined, from Fermyon's GitHub, to your own GitHub account.undefinedEnsure that you are forking the developer repository undefined GitHub account; where you have full editing privileges.","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#1-fork-the-repository"},{"project":"common","title":"Contributing to Docs","subheading":"2. Clone the Fork","content":"Copy the URL from the UI in readiness for running the git clone command.undefinedGo ahead and clone the new fork that you just created (the one which resides in your own GitHub account):$ cd ~\n$ git clone git@github.com:yourusername/developer.git\n$ cd developer","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#2-clone-the-fork"},{"project":"common","title":"Contributing to Docs","subheading":"3. Create New Branch","content":"Create a new branch that will house all of your changes for this specific contribution:$ git checkout -b my_new_branch","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#3-create-new-branch"},{"project":"common","title":"Contributing to Docs","subheading":"4. Add Upstream","content":"Create a new remote for the upstream (a pointer to the original repository to which you are contributing):$ git remote add upstream https://github.com/fermyon/developer","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#4-add-upstream"},{"project":"common","title":"Contributing to Docs","subheading":"5. Code Blocks, Annotations and Table of Contents (ToC)","content":"It is highly recommended that you use either the or the annotation before each of your code blocks, and that each code block defines the appropriate undefined. The annotation can be skipped for code blocks with example code snippets i.e. non-terminal or generic output examples.The selective copy annotation () is intended for use when communicating code and/or CLI commands for the reader to copy and paste. The selective copy annotation allows the reader to see the entire code block (both commands and results) but only copies the lines that start with $ into the reader's clipboard (minus the $) when the user clicks the copy button. For example, copying the following code block will only copy echo \"hello\" into your clipboard, for pasting.$ echo \"hello\"\nhelloundefinedThe no copy annotation () precedes a code block where no copy and pasting of code is intended. If using the no copy attribute please still be sure to add the appropriate syntax highlighting to your code block (for display purposes). For example:Some generic code not intended for copying/pastingMulti-tab code blocks undefined. Examples can be seen in the undefined installer documentation](https://developer.fermyon.com/spin/install#installing-spin) and undefined. The above examples demonstrate how tabs can either represent platforms i.e. Windows, Linux and macOS or represent specific programming languages i.e. Rust, JavaScript and Golang etc. Here is a brief example of how to implement multi-tab code blocks when writing technical documentation for this site, using markdown.The first step to implementing multi-tab code blocks is placing the enable_shortcodes = true configuration at the start of the .md file. Specifically, in the .md file's frontmatter.The markup to create tabs in markdown is as follows{{ tabs \"os\" }}\n\n{{ startTab \"Windows\"}}\n\nTo list files on windows use `dir`\n\n\n\n\\`\\`\\`bash\n$ dir hello_fermyon\n\\`\\`\\`\nand script in windows have the extension `.bat`\n\n\n\n\\`\\`\\`bash\nhello.bat\ntest.bat\n\\`\\`\\`\n\n{{ blockEnd }}\n\n{{ startTab \"Linux\"}}\n\nTo list files on linux use `ls`\n\n\n\n\\`\\`\\`bash\n$ ls\n\\`\\`\\`\n\nand script in linux have the extension `.sh`\n\n\n\n\\`\\`\\`bash\nhello.sh\ntest.sh\n\\`\\`\\`\n\n{{ blockEnd }}\n{{ blockEnd }}undefined: Existing documentation will already be using class names for code block tabs and startTab i.e. {{ tabs \"os\" }} and {{ startTab \"Windows\"}} respectively. Please consult the following tabs and startTab class names that are already in use (before creating your own). If you need to create a new class name (because one does not already exist) please add it to the list below as part of the pull request that contains your code block contribution.undefined:undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedThe next section covers the highly recommended use of ToCs.undefinedIf you create content with many headings it is highly recommended to place a ToC in your markdown file. There are excellent extensions (such as this Visual Studio Code Extension called undefined which will automatically generate your ToC).","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#5-code-blocks-annotations-and-table-of-contents-toc"},{"project":"common","title":"Contributing to Docs","subheading":"6.1 Checking Your Content - Using NPM","content":"Once you are satisfied with your contribution, you can programmatically check your content.If you have not done so already, please go ahead and perform the npm install command; to enable Node dependencies such as markdownlint-cli2. Simply run the following command, from the root of the developer repository:$ npm installWith all Node dependencies installed, you can now check for broken links (which takes several minutes) and also lint your markdown files. Simply run the following command, from the root of the developer repository:$ npm run testundefined Optionally you can run only the linter with the following command:# Example of how to lint all Markdown files in a local folder (in this case the spin folder) \nnpx markdownlint-cli2 content/spin/*.md \\\"#node_modules\\\"\n# Example of how to lint a local Markdown file\nnpx markdownlint-cli2 content/spin/install.md \\\"#node_modules\\\"undefined Whilst the npm run test command (which lints and also programmatically checks all URLs) does take extra time to complete it undefined be utilized before you undefined; preventing the potential pushing of broken URLs to the developer documentation site.","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#61-checking-your-content---using-npm"},{"project":"common","title":"Contributing to Docs","subheading":"6.2 Generating Indexing For Your Content","content":"The developer documentation site implements in-house search and therefore it is recommended to generate a new index each time you update and push changes to the developer documentation repository. This is done simply by using the following command; in the developer directory:$ npm run build-index","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#62-generating-indexing-for-your-content"},{"project":"common","title":"Contributing to Docs","subheading":"6.3 Increasing Search Visibility For Your Content","content":"The built-in search functionality is based on the indexing of individual words in each markdown file, which works well most of the time. However, there are a couple of scenarios where you undefined want to deliberately increase search visibility for your content.undefinedWords in a documentation markdown file may not be words that are searched for by a user. For example, you may write about \"different HTTP listening options\" whereas a user may only ever try to find that specific content using a search phrase like \"alternate port\". If you are aware of typical user search phrases it is always recommended to craft your content to include any predictable user search phrases. However, in the rare case of a mismatch between words in your content and the words a user searches for, you can utilize use the keywords string in the [extra] section of your document's frontmatter to increase visibility. For example, the following code block shows frontmatter that helps a user find your documentation page (when the user searches for port):[extra]\nkeywords = \"port ports\"Adding a word to the keywords string of a page overrides the built-in search functionality by at least one order of magnitude. Adding a word to the keywords string may displace other content, so please use it only if necessary.undefinedThe keywords string takes users to the start of a page. In some cases, this is not ideal. You may want to home in on specific content to resolve a search query.If a search term relates to a specific part of a page, you may use the following syntax anywhere in the body of your markdown file, and the user's search action will direct them straight to the previous heading (nearest heading above the @searchTerm syntax).When using the above @searchTerm feature, please note the following:undefinedundefinedExample: If you search for the word \"homing\", the results will point you to the previous heading in this specific section of the developer documentation.undefined","keywords":"","subsectionKeywords":" homing ","url":"/common/contributing-docs.md#63-increasing-search-visibility-for-your-content"},{"project":"common","title":"Contributing to Docs","subheading":"6.4 The Edit On GitHub Button","content":"Each markdown file in the developer documentation requires a link to its GitHub page for the site to correctly render the \"Edit on GitHub\" button for that page.undefinedIf you create a new markdown file and/or you notice a file without the explicit GitHub URL, please add a URL entry to the [extra] section. For example, the following url is required for this page that you are reading (you can check the raw markdown contents of this page to see this example):[extra]\nurl = \"https://github.com/fermyon/developer/blob/main/content/common/contributing-docs.md\"","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#64-the-edit-on-github-button"},{"project":"common","title":"Contributing to Docs","subheading":"6.5 How To Properly Edit CSS Styles","content":"undefinedDirectly editing .css files is not recommended, because .css files are overwritten. Instead, if you would like to make and test a new/different style please go ahead and update the appropriate .scss file. The following command will automatically update the .css file that is relevant to the .scss file that you are editing:$ npm run stylesThe above command is designed to be run in the background; enabling you to view your design changes (that are reflected in the .css) while you are editing the .scss in real-time. If you are not running this command in the background (i.e. just deliberately regenerating the .css files once), then the above command can be stopped by pressing Ctrl + C.","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#65-how-to-properly-edit-css-styles"},{"project":"common","title":"Contributing to Docs","subheading":"6.6 Checking Your Content - Using Bartholomew's CLI","content":"The Bartholomew Command Line Interface (CLI) Tool is called bart. The bart CLI is a tool that simplifies working with Bartholomew projects (by now you probably already know that undefined is our in-house WebAssembly (Wasm) content management system (CMS) that powers undefined). And this (our official documentation) site. The bart CLI is handy to ensure quality assurance of new and existing content. Installing the CLI is a cinch, so please go ahead and use it when contributing.To build the Bartholomew CLI from source perform the following commands:$ cd ~\n$ git clone https://github.com/fermyon/bartholomew.git\n$ cd ~/bartholomew\n$ make bartOnce built, you will find the bart CLI executable in the ~/bartholomew/target/release directory. However, for convenience it would be a great idea to go ahead and add the bart executable to your system path, for example:$ sudo mv ~/bartholomew/target/release/bart /usr/local/bin/Once installed, you can use the CLI's --help flag to learn more. For example:$ bart --help\nbart 0.6.0\nThe Bartholomew CLI\n\nUSAGE:\n bart \n\nFLAGS:\n -h, --help Prints help information\n -V, --version Prints version information\n\nSUBCOMMANDS:\n calendar Print the content calendar for a Bartholomew website\n check Check content and identify errors or warnings\n help Prints this message or the help of the given subcommand(s)\n new Create a new page or website from a templateLet's take a quick look at how you can use the bart CLI to check any content that you are wanting to contribute.","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#66-checking-your-content---using-bartholomews-cli"},{"project":"common","title":"Contributing to Docs","subheading":"7. Checking Web Pages","content":"The bart CLI can be used to check content by simply passing in the content as a parameter; as shown below:$ bart check content/about.md\n✅ content/about.md","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#7-checking-web-pages"},{"project":"common","title":"Contributing to Docs","subheading":"8. Add Changes","content":"Once your changes have been checked, go ahead and add your changes by moving to a top-level directory, under which your changes exist i.e. cd ~/developer.Add your changes by running the following command, from the root of the developer repository:$ git add","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#8-add-changes"},{"project":"common","title":"Contributing to Docs","subheading":"9. Commit Changes","content":"Before committing, please ensure that your GitHub installation is configured sufficiently so that you can --signoff as part of the git commit command. For example, please ensure that the user.name and user.email are configured in your terminal. You can check if these are set by typing git config --list.If you need to set these values please use the following commands:$ git config user.name \"yourusername\"$ git config user.email \"youremail@somemail.com\"More information can be found at this GitHub documentation page called undefined.Type the following commit command to ensure that you sign off (--signoff), sign the data (-S) - recommended, and also leave a short message (-m):$ git commit -S --signoff -m \"Updating documentation\"undefined","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#9-commit-changes"},{"project":"common","title":"Contributing to Docs","subheading":"10. Push Changes","content":"At this stage, it is a good idea to just quickly check what GitHub thinks the origin is. For example, if we type git remote -v we can see that the origin is our repo; which we a) forked the original repo into and b) which we then cloned to our local disk so that we could edit:$ git remote -vThe above command will return output similar to the following:origin\tgit@github.com:yourusername/developer.git (fetch)\norigin\tgit@github.com:yourusername/developer.git (push)\nupstream\thttps://github.com/fermyon/developer (fetch)\nupstream\thttps://github.com/fermyon/developer (push)Once you are satisfied go ahead and push your changes:$ git push -u origin my_new_branch","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#10-push-changes"},{"project":"common","title":"Contributing to Docs","subheading":"11. Create a Pull Request","content":"If you return to your GitHub repository in your browser, you will notice that a PR has automatically been generated for you.Clicking on the green “Compare and pull request” button will allow you to add a title and description as part of the PR.undefinedYou can also add any information in the textbox provided below the title. For example, screen captures and/or code/console/terminal snippets of your contribution working correctly and/or tests passing etc.Once you have finished creating your PR, please keep an eye on the PR; answering any questions as part of the collaboration process.undefinedThanks for contributing.","keywords":"","subsectionKeywords":"","url":"/common/contributing-docs.md#11-create-a-pull-request"},{"project":"googleea373cbb8f1dde18.md","title":"Google Verification","subheading":"","content":"This is the googleea373cbb8f1dde18.html file that Google can see openly at the root of the website","subsectionKeywords":"","url":"/googleea373cbb8f1dde18"},{"project":"robots.md","title":"Untitled","subheading":"","content":"This is the robots.txt file. It is autogenerated","subsectionKeywords":"","url":"/robots"},{"project":"sitemap.md","title":"Sitemap XML file","subheading":"","content":"This is the autogenerated sitemap. Note that the suffix .xml is replaced with .md by Bartholomew","subsectionKeywords":"","url":"/sitemap"},{"project":"spin","title":"API Support Overview","subheading":"","content":"The following table shows the status of the interfaces Spin provides to applications.| Host Capabilities/Interfaces | Stability | Cloud |\n|----------------------------------------|----------|-------|\n| undefined | Stable | Yes |\n| undefined | Stable | No |\n| undefined | Stable | Yes |\n| undefined | Stable | Yes |\n| undefined | Experimental | No |\n| undefined | Experimental | Yes |\n| undefined | Experimental | Yes |\n| undefined | Stabilizing | Yes |For more information about what is possible in the programming language of your choice, please see our undefined.","subsectionKeywords":"","url":"/spin/api-guides-overview"},{"project":"spin","title":"Building Spin Application Code","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedA Spin application is made up of one or more components. Components are binary Wasm modules; undefined refers to the process of converting your source code into those modules.undefinedBecause most compilers don't target Wasm by default, building Wasm modules often requires special command options, which you may not have at your fingertips.\nWhat's more, when developing a multi-component application, you may need to issue such commands for several components on each iteration.\nDoing this manually can be tedious and error-prone.To make the build process easier, the spin build command allows you to build all the components in one command.undefined","subsectionKeywords":"","url":"/spin/build"},{"project":"spin","title":"Building Spin Application Code","subheading":"Setting Up for spin build","content":"To use spin build, each component that you want to build must specify the command used to build it in spin.toml, as part of its component.build table.[[component]]\nid = \"hello\"\n[component.trigger]\nroute = \"/...\"\n# This is the section you need for `spin build`\n[component.build]\ncommand = \"npm run build\"If you generated the component from a Fermyon-supplied template, the component.build section should be set up correctly for you. You don't need to change or add anything.undefined{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}For Rust applications, you must have the wasm32-wasi target installed:$ rustup target add wasm32-wasiThe build command typically runs cargo build with the wasm32-wasi target and the --release option:[component.build]\ncommand = \"cargo build --target wasm32-wasi --release\"{{ blockEnd }}{{ startTab \"TypeScript\" }}For JavaScript and TypeScript applications, you must have the js2wasm Spin plugin installed:$ spin plugins update\n$ spin plugins install js2wasm --yesIt's normally convenient to put the detailed build instructions in package.json:{\n \"scripts\": {\n \"build\": \"npx webpack --mode=production && mkdir -p target && spin js2wasm -o target/spin-http-js.wasm dist/spin.js\"\n }\n}The build command can then call the NPM script:[component.build]\ncommand = \"npm run build\"{{ blockEnd }}{{ startTab \"Python\" }}For Python applications, you must have the py2wasm Spin plugin installed:$ spin plugins update\n$ spin plugins install py2wasm --yesThe build command then calls spin py2wasm on your application file:[component.build]\ncommand = \"spin py2wasm app -o app.wasm\"{{ blockEnd }}{{ startTab \"TinyGo\" }}For Go applications, you must use the TinyGo compiler, as the standard Go compiler does not yet support the WASI standard. See the undefined.The build command calls TinyGo with the WASI backend and appropriate options:[component.build]\ncommand = \"tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go\"{{ blockEnd }}{{ blockEnd }}undefined","keywords":"","subsectionKeywords":"","url":"/spin/build.md#setting-up-for-spin-build"},{"project":"spin","title":"Building Spin Application Code","subheading":"Running spin build","content":"Once the build commands are set up, running spin build will execute, sequentially, each build command:$ RUST_LOG=spin=trace spin build\n2022-04-25T03:01:56.721630Z INFO spin_build: Executing the build command for component rust-hello.\n Finished release [optimized] target(s) in 0.05s\n2022-04-25T03:01:56.832360Z INFO spin_build: Executing the build command for component rust-static-assets.\n Finished release [optimized] target(s) in 0.02s\n2022-04-25T03:01:56.905424Z INFO spin_build: Executing the build command for component rust-outbound-http.\n Finished release [optimized] target(s) in 0.02s","keywords":"","subsectionKeywords":"","url":"/spin/build.md#running-spin-build"},{"project":"spin","title":"Building Spin Application Code","subheading":"Running the Application After Build","content":"You can pass the --up option to spin build to start the application as soon as the build process completes successfully.This is equivalent to running spin up immediately after spin build. It accepts all the same flags and options that up does. See undefined for details.","keywords":"","subsectionKeywords":"","url":"/spin/build.md#running-the-application-after-build"},{"project":"spin","title":"Building Spin Application Code","subheading":"Overriding the Working Directory","content":"By default, the command to build a component is executed in the directory containing the spin.toml file. If a component's entire build source is under a subdirectory, it is often more convenient to build in that subdirectory rather than try to pass the path to the build command. You can do this by setting the workdir option in the component.build table.For example, consider this Rust component located in subdirectory deep:.\n├── deep\n│   ├── Cargo.toml\n│   └── src\n│   └── lib.rs\n└── spin.tomlTo have the Rust build command run in directory deep, we can set the component's workdir:[component.build]\n# `command` is the normal build command for this language\ncommand = \"cargo build --target wasm32-wasi --release\"\n# This tells Spin to run it in the directory of the build file (in this case Cargo.toml)\nworkdir = \"deep\"undefined","keywords":"","subsectionKeywords":"","url":"/spin/build.md#overriding-the-working-directory"},{"project":"spin","title":"Building Spin Application Code","subheading":"Next Steps","content":"undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/build.md#next-steps"},{"project":"spin","title":"The Spin Registry Cache","subheading":"","content":"undefinedundefined","subsectionKeywords":"","url":"/spin/cache"},{"project":"spin","title":"The Spin Registry Cache","subheading":"The Spin Registry Cache","content":"A running Spin application can undefined and individual component sources via HTTP endpoints. These resources naturally consume network bandwidth. To ensure network efficiency, and to prevent waiting to download the same files every time an application is started, Spin automatically maintains a local spin/registry cache.","keywords":"","subsectionKeywords":"","url":"/spin/cache.md#the-spin-registry-cache"},{"project":"spin","title":"The Spin Registry Cache","subheading":"Clearing the Registry Cache","content":"Clearing the registry cache directory can be done by removing the registry directory entirely. The only direct effect of these actions is that Spin will have to pull again all component sources and static assets.","keywords":"","subsectionKeywords":"","url":"/spin/cache.md#clearing-the-registry-cache"},{"project":"spin","title":"The Spin Registry Cache","subheading":"Viewing Registry Cache Files","content":"undefinedAll downloadable application files that are automatically cached can be found in the following file paths, depending on the operating system being used to run the Spin application:| Platform | Value | Example |\n| :--- | :--- | :--- |\n| Linux | $XDG_CACHE_HOME or $HOME/.cache/spin/registry | /home/alice/.cache/spin/registry |\n| macOS | $HOME/Library/Caches/spin/registry | /Users/Alice/Library/Caches/spin/registry |\n| Windows | {FOLDERID_LocalAppData}/spin/registry | C:\\Users\\Alice\\AppData\\Local/spin/registry |For example inspecting the local spin/registry cache on macOS, (using the tree command) would look like this:$ tree ~/Library/Caches/spin/registry/\n\n├── data\n│   ├── sha256:41a4649a8a8c176133792119cb45a7686767d3fa376ffd656e2ff76a6071fb07\n│   └── sha256:da3fda2db338a73483068072e22f7e7eef27afdbae3db824e130932adce703ba\n├── manifests\n│   └── ghcr.io\n│   └── radu-matei\n│   ├── hello-registries\n│   │   └── latest\n│   │   ├── config.json\n│   │   └── manifest.json\n│   └── spin-openai-demo\n│   └── v1\n│   ├── config.json\n│   └── manifest.json\n└── wasm\n ├── sha256:0b985e7d43e719f34cbb54849759a2f8e7913c0f9b17bf7cb2b3d2458d33859e\n └── sha256:d5f9e1f6b61b90f7404e3800285f7860fe2cfc7d0116023efc370adbb403fe87The data directory contains all static assets referenced from applications distributed with remote registries. The wasm directory contains all component sources referenced either in applications distributed with remote registries, or component sources from HTTP endpoints, directly referenced in spin.toml.undefinedThe manifests directory contains the registry manifests for entire apps distributed with remote registries. They are placed in subdirectories that identify the application based on the registry, repository, and digest (or tag).undefined","keywords":"","subsectionKeywords":"","url":"/spin/cache.md#viewing-registry-cache-files"},{"project":"spin","title":"Contributing to Spin","subheading":"","content":"undefinedundefinedundefinedWe are delighted that you are interested in making Spin better! Thank you! This\ndocument will guide you through making your first contribution to the project.\nWe welcome and appreciate contributions of all types — opening issues, fixing\ntypos, adding examples, one-liner code fixes, tests, or complete features.First, any contribution and interaction on any Fermyon project MUST follow our\nundefined. Thank you for being\npart of an inclusive and open community!If you plan on contributing anything complex, please go through the issue and PR\nqueues first to make sure someone else has not started working on it. If it\ndoesn't exist already, please open an issue so you have a chance to get feedback\nfrom the community and the maintainers before you start working on your feature.","subsectionKeywords":"","url":"/spin/contributing-spin"},{"project":"spin","title":"Contributing to Spin","subheading":"Making Code Contributions to Spin","content":"The following guide is intended to make sure your contribution can get merged as\nsoon as possible. First, make sure you have the following prerequisites\nconfigured:undefinedundefinedundefinedundefinedundefinedOnce you have set up the prerequisites and identified the contribution you want\nto make to Spin, make sure you can correctly build the project:# clone the repository\n$ git clone https://github.com/fermyon/spin && cd spin\n# add a new remote pointing to your fork of the project\n$ git remote add fork https://github.com//spin\n# create a new branch for your work\n$ git checkout -b \n\n# build a release version of the Spin CLI\n$ cargo build --release\n# make sure compilation is successful\n$ ./target/release/spin --help\n\n# run the tests and make sure they pass\n$ make testNow you should be ready to start making your contribution. To familiarize\nyourself with the Spin project, please read the\nundefined. Since most of Spin is implemented in\nRust, we try to follow the common Rust coding conventions (keep an eye on the\nrecommendations from Clippy!). If applicable, add unit or integration tests to\nensure your contribution is correct.","keywords":"","subsectionKeywords":"","url":"/spin/contributing-spin.md#making-code-contributions-to-spin"},{"project":"spin","title":"Contributing to Spin","subheading":"Before You Commit","content":"undefinedundefinedundefinedundefinedSpin enforces lints and tests as part of continuous integration - running them locally will save you a round-trip to your pull request!If everything works locally, you're ready to commit your changes.","keywords":"","subsectionKeywords":"","url":"/spin/contributing-spin.md#before-you-commit"},{"project":"spin","title":"Contributing to Spin","subheading":"Committing and Pushing Your Changes","content":"We require commits to be signed both with an email address and with a GPG signature.undefined$ git commit -S -s -m \"\"undefinedWe try to only keep useful changes as separate commits — if you prefer to commit\noften, please\nundefined\nbefore opening a pull request.Once you are happy with your changes you can push the branch to your fork:# \"fork\" is the name of the git remote pointing to your fork\n$ git push forkNow you are ready to create a pull request. Thank you for your contribution","keywords":"","subsectionKeywords":"","url":"/spin/contributing-spin.md#committing-and-pushing-your-changes"},{"project":"spin","title":"Deploying Spin Applications to Fermyon","subheading":"","content":"undefinedundefinedundefined","subsectionKeywords":"","url":"/spin/deploying-to-fermyon"},{"project":"spin","title":"Deploying Spin Applications to Fermyon","subheading":"Deploying Microservices & Web Apps","content":"undefined is the frictionless WebAssembly platform for deploying\nmicroservices and web apps. With Fermyon, you can deploy your spin applications onto a server in\nmoments.","keywords":"","subsectionKeywords":"","url":"/spin/deploying-to-fermyon.md#deploying-microservices-web-apps"},{"project":"spin","title":"Deploying Spin Applications to Fermyon","subheading":"Running on Your Workstation","content":"For instructions guiding you through running the Fermyon platform on your development workstation,\nfollow undefined.","keywords":"","subsectionKeywords":"","url":"/spin/deploying-to-fermyon.md#running-on-your-workstation"},{"project":"spin","title":"Deploying Spin Applications to Fermyon","subheading":"Running on AWS","content":"For instructions guiding you through running the Fermyon platform on AWS, follow\nundefined.","keywords":"","subsectionKeywords":"","url":"/spin/deploying-to-fermyon.md#running-on-aws"},{"project":"spin","title":"Designing Spin Applications","subheading":"","content":"undefinedundefined","subsectionKeywords":"","url":"/spin/designing-spin-apps"},{"project":"spin","title":"Designing Spin Applications","subheading":"Built With Spin","content":"Spin can be used to build many different types of applications. The following blog articles show how different applications are built with Spin:undefinedThe 'Building a Social App with Spin' series covers the process and decision-making of building an authenticated, dynamic, database-backed application and API, right the way from downloading Spin and setting up the project to deploying the finished application to the cloud.undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedThe 'Finicky Whiskers' series covers the journey of creating a game with an architecture designed to undefined and showcase how WebAssembly modules can be started, executed, and shut down in the blink of an eye.undefinedundefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/designing-spin-apps.md#built-with-spin"},{"project":"spin","title":"Designing Spin Applications","subheading":"Next Steps","content":"undefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/designing-spin-apps.md#next-steps"},{"project":"spin","title":"Publishing and Distribution","subheading":"","content":"undefinedundefinedundefinedundefinedIf you would like to publish a Spin application, so that other users can run it, you can do so using a undefined.{{ details \"What's all this about containers?\" \"The registry protocol was originally created to publish and distribute Docker containers. Over time, registries have evolved to host other kinds of artifact - see undefined for more information. However, the term remains, in services such as GitHub Container Registry or AWS Elastic Container Registry, and in the generic description undefined. When you use a 'container' registry to publish and distribute Spin applications, there are no actual containers involved at all!\" }}Many cloud services offer public registries. Examples include GitHub Container Registry, Docker Hub, or Amazon Elastic Container Registry. These support both public and private distribution. You can also run your own registry using open source software.","subsectionKeywords":"","url":"/spin/distributing-apps"},{"project":"spin","title":"Publishing and Distribution","subheading":"Logging Into a Registry","content":"Before you can publish to a registry, or run applications whose registry artifacts are private, you must log in to the registry. This example shows logging into the GitHub Container Registry, ghcr.io:$ spin registry login ghcr.ioIf you don't provide any options to spin registry login, it prompts you for a username and password.","keywords":"","subsectionKeywords":"","url":"/spin/distributing-apps.md#logging-into-a-registry"},{"project":"spin","title":"Publishing and Distribution","subheading":"Logging In Using a Token","content":"In a non-interactive environment such as GitHub Actions, you will typically log in using a token configured in the environment settings, rather than a password. To do this, use the --password-stdin flag, and echo the token value to the login command's standard input. This example shows logging into GHCR from a GitHub action:$ echo \"$\\{{ secrets.GITHUB_TOKEN }}\" | spin registry login ghcr.io --username $\\{{ github.actor }} --password-stdinOther environments will have different ways of referring to the token and user but the pattern remains the same.","keywords":"","subsectionKeywords":"","url":"/spin/distributing-apps.md#logging-in-using-a-token"},{"project":"spin","title":"Publishing and Distribution","subheading":"Fallback Credentials","content":"If you have logged into a registry using docker login, but not using spin registry login, Spin will fall back to your Docker credentials.","keywords":"","subsectionKeywords":"","url":"/spin/distributing-apps.md#fallback-credentials"},{"project":"spin","title":"Publishing and Distribution","subheading":"Publishing a Spin Application to a Registry","content":"To publish an application to a registry, use the spin registry push command. You must provide a undefined for the published application. This is a string whose format is defined by the registry standard, and generally consists of //:. (In specific circumstances you may be able to omit the username and/or the version. If you want more detail on references, see the OCI documentation.)undefinedHere is an example of pushing an application to GHCR:$ spin registry push ghcr.io/alyssa-p-hacker/hello-world:v1\nPushed with digest sha256:06b19Notice that the username is part of the reference; the registry does not infer it from the login. Also notice that the version is specified explicitly; Spin does not infer it from the spin.toml file.undefined","keywords":"","subsectionKeywords":"","url":"/spin/distributing-apps.md#publishing-a-spin-application-to-a-registry"},{"project":"spin","title":"Publishing and Distribution","subheading":"Running Published Applications","content":"To run a published application from a registry, use spin up -f and pass the registry reference:$ spin up -f ghcr.io/alyssa-p-hacker/hello-world:v1undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/distributing-apps.md#running-published-applications"},{"project":"spin","title":"Publishing and Distribution","subheading":"Running Published Applications by Digest","content":"Registry versions are mutable; that is, the owner of an application can change which build the :v1 label points to at any time. If you want to run a specific build of the package, you can refer to it by undefined. This is similar to a Git commit hash: it is immutable, meaning the same digest always gets the exact same data, no matter what the package owner does. To do this, use the @sha256:... syntax instead of the :v... syntax:$ spin up -f ghcr.io/alyssa-p-hacker/hello-world@sha256:06b19","keywords":"","subsectionKeywords":"","url":"/spin/distributing-apps.md#running-published-applications-by-digest"},{"project":"spin","title":"Publishing and Distribution","subheading":"Pulling a Published Application","content":"spin up automatically downloads the application from the registry. If you want to manually download the application, without running it, use the spin registry pull command:$ spin registry pull ghcr.io/alyssa-p-hacker/hello-world:v1\n$ spin registry pull ghcr.io/alyssa-p-hacker/hello-world@sha256:06b19undefined","keywords":"","subsectionKeywords":"","url":"/spin/distributing-apps.md#pulling-a-published-application"},{"project":"spin","title":"Publishing and Distribution","subheading":"Signing Spin Applications and Verifying Signatures","content":"Because Spin uses the container registry standards to distribute applications, it can also take advantage of tooling built around those standards. Here is an example of using undefined to sign and verify a Spin application:# Push your Spin application to any registry that supports the OCI registry artifacts,\n# such as the GitHub Container Registry, Docker Hub, Azure ACR, or AWS ECR.\n$ spin registry push ghcr.io/alyssa-p-hacker/hello-world:v1\n\n# You can now sign your Spin app using Cosign (or any other tool that can sign\n# OCI registry objects).\n$ cosign sign ghcr.io/alyssa-p-hacker/hello-world@sha256:06b19\nGenerating ephemeral keys...\nRetrieving signed certificate...\ntlog entry created with index: 12519542\nPushing signature to: ghcr.io/alyssa-p-hacker/hello-world\n\n# Someone interested in your application can now use Cosign to verify the signature\n# before running the application.\n$ cosign verify ghcr.io/alyssa-p-hacker/hello-world@sha256:06b19\nVerification for ghcr.io/alyssa-p-hacker/hello-world@sha256:06b19 --\nThe following checks were performed on each of these signatures:\n - The cosign claims were validated\n - Existence of the claims in the transparency log was verified offline\n - Any certificates were verified against the Fulcio roots.\n\n# The consumer of your app can now run it from the registry.\n$ spin up -f ghcr.io/alyssa-p-hacker/hello-world@sha256:06b19undefined","keywords":"","subsectionKeywords":"","url":"/spin/distributing-apps.md#signing-spin-applications-and-verifying-signatures"},{"project":"spin","title":"Dynamic and Runtime Application Configuration","subheading":"","content":"undefinedundefinedundefinedundefinedSpin applications may define custom configuration which can be looked up by\ncomponent code via the undefined.","subsectionKeywords":"","url":"/spin/dynamic-configuration"},{"project":"spin","title":"Dynamic and Runtime Application Configuration","subheading":"Custom Config Variables","content":"Application-global custom config variables are defined in the top-level [variables]\nsection. These entries aren't accessed directly by components but are referenced\nby undefined value templates. Each entry must\neither have a default value or be marked as required = true. \"Required\" entries\nmust be undefined with a value.Configuration keys may only contain lowercase letters and underscores between letters:[variables]\napi_host = { default = \"api.example.com\" }\napi_key = { required = true }","keywords":"","subsectionKeywords":"","url":"/spin/dynamic-configuration.md#custom-config-variables"},{"project":"spin","title":"Dynamic and Runtime Application Configuration","subheading":"Component Custom Config","content":"The configuration entries available to a component are listed in its\n[component.config] section. Configuration values may reference\nundefined with simple\nundefined-inspired string templates:[[component]]\n# ...\n[component.config]\napi_base_url = \"https://{{ api_host }}/v1\"\napi_key = \"{{ api_key }}\"","keywords":"","subsectionKeywords":"","url":"/spin/dynamic-configuration.md#component-custom-config"},{"project":"spin","title":"Dynamic and Runtime Application Configuration","subheading":"Custom Config Providers","content":"undefined values may be set at runtime by\nconfig \"providers\". Currently, there are two providers: the environment\nvariable provider and vault config provider.","keywords":"","subsectionKeywords":"","url":"/spin/dynamic-configuration.md#custom-config-providers"},{"project":"spin","title":"Dynamic and Runtime Application Configuration","subheading":"Environment Variable Provider","content":"The environment variable provider which gets config values from the spin process's\nenvironment (undefined the component environment). Config keys are translated\nto environment variables by upper-casing and prepending with SPIN_CONFIG_:$ export SPIN_CONFIG_API_KEY = \"1234\" # Sets the `api_key` value.\n$ spin up","keywords":"","subsectionKeywords":"","url":"/spin/dynamic-configuration.md#environment-variable-provider"},{"project":"spin","title":"Dynamic and Runtime Application Configuration","subheading":"Vault Config Provider","content":"The Vault config provider gets secret values from undefined.\nCurrently, only undefined is supported.\nYou can set up v2 kv secret engine at any mount point and give Vault information in the undefined file:[[config_provider]]\ntype = \"vault\"\nurl = \"http://127.0.0.1:8200\"\ntoken = \"root\"\nmount = \"secret\"","keywords":"","subsectionKeywords":"","url":"/spin/dynamic-configuration.md#vault-config-provider"},{"project":"spin","title":"Dynamic and Runtime Application Configuration","subheading":"Vault Config Provider Example","content":"undefinedundefined$ vault server -dev -dev-root-token-id rootundefined$ export VAULT_TOKEN=root\n$ export VAULT_ADDR=http://127.0.0.1:8200\n$ vault kv put secret/password value=\"test_password\"\n$ vault kv get secret/passwordundefinedundefined$ spin build\n$ spin up --runtime-config-file runtime_config.tomlundefined$ curl -i http://127.0.0.1:3000\nHTTP/1.1 200 OK\ncontent-length: 26\ndate: Tue, 18 Oct 2022 12:34:40 GMT\n\nGot password test_password","keywords":"","subsectionKeywords":"","url":"/spin/dynamic-configuration.md#vault-config-provider-example"},{"project":"spin","title":"Dynamic and Runtime Application Configuration","subheading":"Runtime Configuration","content":"Runtime configuration contains information for the selected config provider, such as the undefined.\nYou can supply runtime configuration by providing a value for the --runtime-config-file flag when invoking the spin up command.","keywords":"","subsectionKeywords":"","url":"/spin/dynamic-configuration.md#runtime-configuration"},{"project":"spin","title":"Dynamic and Runtime Application Configuration","subheading":"Key Value Store Runtime Configuration","content":"Spin provides built-in key-value storage. This storage is backed by an SQLite database embedded in Spin by default. However, the Spin runtime configuration file (runtime-config.toml) can be updated to not only modify the SQLite configuration but also choose to use a different backing store. The only store options are the embedded SQLite database or an external Redis database.The following is an example of how an application's runtime-config.toml file can be configured to use Redis instead. Note the type and url values, which are set to redis and the URL of the Redis host, respectively:[key_value_store.default]\ntype = \"redis\"\nurl = \"redis://localhost\"Whilst a single default store may be sufficient for certain application use cases, each Spin application can be configured to support multiple stores of any type, as shown in the runtime-config.toml file below:undefined# This defines a new store named user_data\n[key_value_store.user_data]\ntype = \"spin\" \npath = \".spin/user_data.db\"\n\n# This defines a new store named other_data backed by a Redis database\n[key_value_store.other_data]\ntype = \"redis\"\nurl = \"redis://localhost\"You must individually grant each component access to the stores that it needs to use. To do this, use the component.key_value_stores entry in the component manifest within spin.toml. See undefined for more details.","keywords":"","subsectionKeywords":"","url":"/spin/dynamic-configuration.md#key-value-store-runtime-configuration"},{"project":"spin","title":"Extending and Embedding Spin","subheading":"","content":"undefinedundefinedSpin currently implements triggers and application models for:undefinedundefinedThe Spin internals and execution context (the part of Spin executing\ncomponents) are agnostic of the event source and application model.\nIn this document, we will explore how to extend Spin with custom event sources\n(triggers) and application models built on top of the WebAssembly component\nmodel, as well as how to embed Spin in your application.In this article, we will build a Spin trigger to run the applications based on a\ntimer, executing Spin components at a configured time interval.The current application types that can be implemented with Spin have entry points\ndefined using\nundefined:// The entry point for an HTTP handler.\nhandle-http-request: function(req: request) -> response\n\n// The entry point for a Redis handler.\nhandle-redis-message: function(msg: payload) -> expected<_, error>The entry point we want to execute for our timer trigger takes a string as its\nonly argument (the trigger will populate that with the current date and time),\nand it expects a string as the only return value. This is purposefully chosen\nto be a simple function signature:// examples/spin-timer/spin-timer.wit\nhandle-timer-request: function(msg: string) -> stringThis is the function that all components executed by the timer trigger must\nimplement, and which is used by the timer executor when instantiating and\ninvoking the component.Let's have a look at building the timer trigger:// examples/spin-timer/src/main.rs\nwit_bindgen_wasmtime::import!({paths: [\"spin-timer.wit\"], async: *});\ntype ExecutionContext = spin_engine::ExecutionContext;\n\n/// A custom timer trigger that executes a component on every interval.\n#[derive(Clone)]\npub struct TimerTrigger {\n /// The interval at which the component is executed.\n pub interval: Duration,\n /// The Spin execution context.\n engine: Arc,\n}A few important things to note from the start:undefinedundefinedundefinedFinally, whenever there is a new event (in the case of our timer-based trigger\nevery n seconds), we execute the entry point of a selected component:/// Execute the first component in the application manifest.\nasync fn handle(&self, msg: String) -> Result<()> {\n // create a new Wasmtime store and instance based on the first component's WebAssembly module.\n let (mut store, instance) =\n self.engine\n .prepare_component(&self.app.components[0].id, None, None, None, None)?;\n\n // spawn a new thread and call the entry point function from the WebAssembly module \n let res = spawn_blocking(move || -> Result {\n // use the auto-generated WIT bindings to get the Wasm exports and call the `handle-timer-request` function.\n let t = spin_timer::SpinTimer::new(&mut store, &instance, |host| {\n host.data.as_mut().unwrap()\n })?;\n Ok(t.handle_timer_request(&mut store, &msg)?)\n })\n .await??;\n // do something with the result.\n log::info!(\"{}\\n\", res);\n Ok(())\n}A few notes:undefinedundefinedundefinedThis is very similar to how the undefined and undefined\ntriggers are implemented, and it is the recommended way to extend Spin with your\nown trigger and application model.Writing components for the new trigger can be done by using the\nundefined from\nRust and other supported languages (see undefined):// automatically generate Rust bindings that help us implement the \n// `handle-timer-request` function that the trigger will execute.\nwit_bindgen_rust::export!(\"../spin-timer.wit\");\n...\nfn handle_timer_request(msg: String) -> String {\n format!(\"ECHO: {}\", msg)\n}Components can be compiled to WebAssembly, then used from a spin.toml\napplication manifest.Embedding the new trigger in a Rust application is done by creating a new trigger\ninstance, then calling its run function:// app() is a utility function that generates a complete application configuration.\nlet trigger = TimerTrigger::new(Duration::from_secs(1), app()).await?;\n// run the trigger indefinitely\ntrigger.run().awaitundefinedIn this example, we built a simple timer trigger — building more complex triggers\nwould also involve updating the Spin application manifest, and extending\nthe application-level trigger configuration, as well as component-level\ntrigger configuration (an example of component-level trigger configuration\nfor this scenario would be each component being able to define its own\nindependent time interval for scheduling the execution).","subsectionKeywords":"","url":"/spin/extending-and-embedding"},{"project":"spin","title":"Extending and Embedding Spin","subheading":"Other Ways to Extend and Use Spin","content":"Besides building custom triggers, the internals of Spin could also be used independently:undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/extending-and-embedding.md#other-ways-to-extend-and-use-spin"},{"project":"spin","title":"Building Spin components in Go","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/spin/go-components"},{"project":"spin","title":"Building Spin components in Go","subheading":"Versions","content":"TinyGo 0.25.x is recommended, which requires Go 1.16.x or newer.undefined","keywords":"","subsectionKeywords":"","url":"/spin/go-components.md#versions"},{"project":"spin","title":"Building Spin components in Go","subheading":"HTTP Components","content":"In Spin, HTTP components are triggered by the occurrence of an HTTP request, and\nmust return an HTTP response at the end of their execution. Components can be\nbuilt in any language that compiles to WASI, and Go has improved support for\nwriting applications, through its SDK.Building a Spin HTTP component using the Go SDK means writing a single function,\ninit — below is a complete implementation for such a component:// A Spin component written in Go that returns \"Hello, Fermyon!\"\npackage main\n\nimport (\n \"fmt\"\n \"net/http\"\n\n spinhttp \"github.com/fermyon/spin/sdk/go/http\"\n)\n\nfunc init() {\n spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {\n w.Header().Set(\"Content-Type\", \"text/plain\")\n fmt.Fprintln(w, \"Hello Fermyon!\")\n })\n}\n\nfunc main() {}The important things to note in the implementation above:undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/go-components.md#http-components"},{"project":"spin","title":"Building Spin components in Go","subheading":"Sending Outbound HTTP Requests","content":"If allowed, Spin components can send outbound requests to HTTP endpoints. Let's\nsee an example of a component that makes a request to\nundefined and\ninserts a custom header into the response before returning:// A Spin component written in Go that sends a request to an API\n// with random dog facts.\n\npackage main\n\nimport (\n \"bytes\"\n \"fmt\"\n \"net/http\"\n \"os\"\n\n spinhttp \"github.com/fermyon/spin/sdk/go/http\"\n)\n\nfunc init() {\n spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {\n r, _ := spinhttp.Get(\"https://some-random-api.ml/facts/dog\")\n\n fmt.Fprintln(w, r.Body)\n fmt.Fprintln(w, r.Header.Get(\"content-type\"))\n\n // `spin.toml` is not configured to allow outbound HTTP requests to this host,\n // so this request will fail.\n if _, err := spinhttp.Get(\"https://fermyon.com\"); err != nil {\n fmt.Fprintf(os.Stderr, \"Cannot send HTTP request: %v\", err)\n }\n })\n}\n\nfunc main() {}The component can be built using the tingygo toolchain:$ tinygo build -wasm-abi=generic -target=wasi -no-debug -o main.wasm main.goBefore we can execute this component, we need to add the\nsome-random-api.ml domain to the application manifest allowed_http_hosts\nlist containing the list of domains the component is allowed to make HTTP\nrequests to:# spin.toml\nspin_manifest_version = \"1\"\nname = \"spin-hello-tinygo\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"1.0.0\"\n\n[[component]]\nid = \"tinygo-hello\"\nsource = \"main.wasm\"\nallowed_http_hosts = [ \"some-random-api.ml\" ]\n[component.trigger]\nroute = \"/hello\"undefinedRunning the application using spin up --file spin.toml will start the HTTP\nlistener locally (by default on localhost:3000), and our component can\nnow receive requests in route /hello:$ curl -i localhost:3000/hello\nHTTP/1.1 200 OK\ncontent-type: text/plain; charset=utf-8\nserver: spin/0.1.0\ncontent-length: 85\ndate: Fri, 18 Mar 2022 23:27:33 GMT\n\n{{\"fact\":\"Seventy percent of people sign their dog's name on their holiday cards.\"}}undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/go-components.md#sending-outbound-http-requests"},{"project":"spin","title":"Building Spin components in Go","subheading":"Redis Components","content":"Besides the HTTP trigger, Spin has built-in support for a Redis trigger, which\nwill connect to a Redis instance and will execute components for new messages\non the configured channels.undefinedWriting a Redis component in Go also takes advantage of the SDK:package main\n\nimport (\n \"fmt\"\n\n \"github.com/fermyon/spin/sdk/go/redis\"\n)\n\nfunc init() {\n // redis.Handle() must be called in the init() function.\n redis.Handle(func(payload []byte) error {\n fmt.Println(\"Payload::::\")\n fmt.Println(string(payload))\n return nil\n })\n}\n\n// main function must be included for the compiler but is not executed.\nfunc main() {}The manifest for a Redis application must contain the address of the Redis instance:spin_manifest_version = \"1\"\nname = \"spin-redis\"\ntrigger = { type = \"redis\", address = \"redis://localhost:6379\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"echo-message\"\nsource = \"main.wasm\"\n[component.trigger]\nchannel = \"messages\"\n[component.build]\ncommand = \"tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go\"The application will connect to redis://localhost:6379, and for every new message\non the messages channel, the echo-message component will be executed:# first, start redis-server on the default port 6379\n$ redis-server --port 6379\n# then, start the Spin application\n$ spin build --up\nINFO spin_redis_engine: Connecting to Redis server at redis://localhost:6379\nINFO spin_redis_engine: Subscribed component 0 (echo-message) to channel: messagesFor every new message on the messages channel:$ redis-cli\n127.0.0.1:6379> publish messages \"Hello, there!\"Spin will instantiate and execute the component:INFO spin_redis_engine: Received message on channel \"messages\"\nPayload::::\nHello, there!","keywords":"","subsectionKeywords":"","url":"/spin/go-components.md#redis-components"},{"project":"spin","title":"Building Spin components in Go","subheading":"Storing Data in Redis From Go Components","content":"Using the Spin's Go SDK, you can use the Redis key/value store to publish\nmessages to Redis channels. This can be used from both HTTP and Redis triggered\ncomponents.Let's see how we can use the Go SDK to connect to Redis:package main\n\nimport (\n \"net/http\"\n \"os\"\n\n spin_http \"github.com/fermyon/spin/sdk/go/http\"\n \"github.com/fermyon/spin/sdk/go/redis\"\n)\n\nfunc init() {\n // handler for the http trigger\n spin_http.Handle(func(w http.ResponseWriter, r *http.Request) {\n\n // addr is the environment variable set in `spin.toml` that points to the\n // address of the Redis server.\n addr := os.Getenv(\"REDIS_ADDRESS\")\n\n // channel is the environment variable set in `spin.toml` that specifies\n // the Redis channel that the component will publish to.\n channel := os.Getenv(\"REDIS_CHANNEL\")\n\n // payload is the data publish to the redis channel.\n payload := []byte(`Hello redis from tinygo!`)\n\n if err := redis.Publish(addr, channel, payload); err != nil {\n http.Error(w, err.Error(), http.StatusInternalServerError)\n return\n }\n\n // set redis `mykey` = `myvalue`\n if err := redis.Set(addr, \"mykey\", []byte(\"myvalue\")); err != nil {\n http.Error(w, err.Error(), http.StatusInternalServerError)\n return\n }\n\n // get redis payload for `mykey`\n if payload, err := redis.Get(addr, \"mykey\"); err != nil {\n http.Error(w, err.Error(), http.StatusInternalServerError)\n } else {\n w.Write([]byte(\"mykey value was: \"))\n w.Write(payload)\n }\n })\n}\n\nfunc main() {}This HTTP component demonstrates fetching a value from Redis by key, setting a\nkey with a value, and publishing a message to a Redis channel. The component is\ntriggered by an HTTP request served on the route configured in the spin.toml:[[component]]\nenvironment = { REDIS_ADDRESS = \"redis://127.0.0.1:6379\", REDIS_CHANNEL = \"messages\" }\n[component.trigger]\nroute = \"/publish\"This HTTP component can be paired with a Redis component, triggered on new\nmessages on the messages Redis channel.undefined","keywords":"","subsectionKeywords":"","url":"/spin/go-components.md#storing-data-in-redis-from-go-components"},{"project":"spin","title":"Building Spin components in Go","subheading":"Using Go Packages in Spin Components","content":"Any\nundefined that can be imported in TinyGo and that compiles to\nWASI can be used when implementing a Spin component.undefined","keywords":"","subsectionKeywords":"","url":"/spin/go-components.md#using-go-packages-in-spin-components"},{"project":"spin","title":"Making HTTP Requests","subheading":"","content":"undefinedundefinedSpin provides an interface for you to make outgoing HTTP requests.{{ details \"Why do I need a Spin interface? Why can't I just use my language's HTTP library?\" \"The current version of the WebAssembly System Interface (WASI) doesn't provide a sockets interface, so HTTP libraries can't be built to Wasm. The Spin interface means Wasm modules can bypass this limitation by asking Spin to perform the HTTP request on their behalf.\" }}","subsectionKeywords":"","url":"/spin/http-outbound"},{"project":"spin","title":"Making HTTP Requests","subheading":"Using HTTP From Applications","content":"The Spin SDK surfaces the Spin HTTP interface to your language. The interface contains only one operation:| Operation | Parameters | Returns | Behavior |\n|------------|------------|---------|----------|\n| request | request record | response record | Sends the given HTTP request, and returns the response. |The request record specifies:| Field | Type | Meaning |\n|------------|----------|------------------|\n| method | enum | The HTTP method for the request, e.g. GET, POST, DELETE, etc. |\n| uri | string | The URI to which to make the request |\n| headers | list of key-value string pairs | The request headers |\n| body | bytes | Optional request body |undefinedThe response record contains:| Field | Type | Meaning |\n|------------|----------|------------------|\n| status | integer | The HTTP status code of the response, e.g. 200, 404, etc. |\n| headers | list of key-value string pairs | The response headers, if any |\n| body | bytes | The response body, if any |The exact detail of calling the request operation from your application depends on your language:{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}HTTP functions are available in the spin_sdk::outbound_http module. The function is named send_request. It takes a spin_sdk::http::Request and returns a spin_sdk::http::Response. Both of these types are specializations of the Request and Response types from the http crate, and have all their behaviour and methods; the Spin SDK maps them to the underlying Wasm interface. For example:use spin_sdk::http::{Request, Response};\n\nlet request = http::Request::builder()\n .method(\"POST\")\n .uri(\"https://example.com/users\")\n .body(Some(json_text.into()))?;\n\nlet response = spin_sdk::outbound_http::send_request(request)?;\nprintln!(\"Status: {}\", response.status().as_str());undefinedundefinedundefinedYou can find a complete example for using outbound HTTP in the undefined.{{ blockEnd }}{{ startTab \"TypeScript\"}}HTTP operations are available via the standard JavaScript fetch function. The Spin runtime maps this to the underlying Wasm interface. For example:const response = await fetch(\"https://example.com/users\");undefinedundefinedYou can find a complete example of using outbound HTTP in the JavaScript SDK repository on GitHub (undefined, undefined).{{ blockEnd }}{{ startTab \"Python\"}}HTTP functions and classes are available in the spin_http module. The function name is http_send. The request type is Request, and the response type is Response. For example:from spin_http import Request, Response, http_send\n\nresponse = http_send(\n Request(\"GET\", \"https://some-random-api.ml/facts/dog\", [], None))undefinedundefinedundefinedundefinedYou can find a complete example for using outbound HTTP in the undefined.{{ blockEnd }}{{ startTab \"TinyGo\"}}HTTP functions are available in the github.com/fermyon/spin/sdk/go/http package. The general function is named Send, but the Go SDK also surfaces individual functions, with request-specific parameters, for the Get and Post operations. For example:import (\n\tspinhttp \"github.com/fermyon/spin/sdk/go/http\"\n)\n\nres1, err1 := spinhttp.Get(\"https://some-random-api.ml/facts/dog\")\nres2, err2 := spinhttp.Post(\"https://example.com/users\", \"application/json\", json)\n\nrequest, err := http.NewRequest(\"PUT\", \"https://example.com/users/1\", bytes.NewBufferString(user1))\nrequest.Header.Add(\"content-type\", \"application/json\")\nres3, err3 := spinhttp.Send(req)\nundefinedundefinedundefinedundefinedYou can find a complete example for using outbound HTTP in the undefined.{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/http-outbound.md#using-http-from-applications"},{"project":"spin","title":"Making HTTP Requests","subheading":"Granting HTTP Permissions to Components","content":"By default, Spin components are not allowed to make outgoing HTTP requests. This follows the general Wasm rule that modules must be explicitly granted capabilities, which is important to sandboxing. To grant a component permission to make HTTP requests to a particular host, use the allowed_http_hosts field in the component manifest:[component]\nallowed_http_hosts = [ \"dog-facts.example.com\", \"api.example.com:8080\" ]The Wasm module can make HTTP requests undefined to the specified hosts. If a port is specified, the module can make requests only to that port; otherwise, the module can make requests only on the default HTTP and HTTPS ports. Requests to other hosts (or ports) will fail with an error.For development-time convenience, you can also pass the string \"insecure:allow-all\" in the allowed_http_hosts collection. This allows the Wasm module to make HTTP requests to undefined host and on any port. However, once you've determined which hosts your code needs, you should remove this string and list the hosts instead. Other Spin implementations may restrict host access and disallow components that ask to connect to anything and everything!","keywords":"","subsectionKeywords":"","url":"/spin/http-outbound.md#granting-http-permissions-to-components"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedHTTP applications are an important workload in event-driven environments,\nand Spin has built-in support for creating and running HTTP\ncomponents. This page covers Spin options that are specific to HTTP applications.The HTTP trigger in Spin is a web server. It listens for incoming requests and\nbased on the undefined, it routes them to a\ncomponent, which returns an HTTP response.","subsectionKeywords":"","url":"/spin/http-trigger"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Specifying an Application as HTTP","content":"Every Spin application has a trigger specified in the manifest, which declares the type of events it responds to.\nFor HTTP applications, the application trigger has type = \"http\":# spin.toml\ntrigger = { type = \"http\", base = \"/\" }The HTTP trigger also requires a base field. Spin interprets each component route as relative to this route. In most cases, you can set this to \"/\", the base of the Web server, meaning Spin applies no prefix to component routes.undefinedIn addition, each component must have HTTP-specific configuration in its [component.trigger] table.","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#specifying-an-application-as-http"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Mapping a Route to a Component","content":"Each component handles one route, specified in the route field of the component trigger table.The route may be undefined or undefined.An undefined route matches only the given route. This is the default behavior. For example, /cart matches only /cart, and not /cart/checkout:# Run the `cart.wasm` module when the application receives a request to `/cart`...\n[[component]]\nid = \"cart\"\nsource = \"cart.wasm\"\n[component.trigger]\nroute = \"/cart\"\n\n# ...and the `checkout.wasm` module for `/cart/checkout`\n[[component]]\nid = \"checkout\"\nsource = \"checkout.wasm\"\n[component.trigger]\nroute = \"/cart/checkout\"A undefined route matches the given route and any route under it. A route is a wildcard if it ends in /.... For example, /users/... matches /users, /users/1, /users/1/edit, and so on. Any of these routes will run the mapped component.undefined[[component]]\nid = \"user-manager\"\nsource = \"users.wasm\"\n# Run the `users.wasm` module when the application receives a request to `/users`\n# or any path beginning with `/users/`\n[component.trigger]\nroute = \"/users/...\"","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#mapping-a-route-to-a-component"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Routing with an Application base","content":"If the application base is \"/\" then all component routes are matched exactly as given.If base contains a non-root path, this is prefixed to all component routes,\nexact or wildcard.For example, suppose the application base path is base = \"/shop\". Then a component with route = \"/cart\" will be executed for requests to /shop/cart. Similarly, a component with route = \"/users/...\" will be executed for requests to /shop/users, /shop/users/1, /shop/users/1/edit and so on.","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#routing-with-an-application-base"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Resolving Overlapping Routes","content":"If multiple components could potentially handle the same request based on their\ndefined routes, the component whose route has the longest matching prefix\ntakes precedence. This also means that exact matches take precedence over wildcard matches.In the following example, requests starting with the /users/ prefix (e.g. /users/1)\nwill be handled by user-manager, even though it is also matched by the shop route, because the /users prefix is longer than /.\nBut requests to /users/admin will be handled by the admin component, not user-manager, because that is a more exact match still:# spin.toml\n\ntrigger = { type = \"http\", base = \"/\"}\n\n[[component]]\nid = \"user-manager\"\n[component.trigger]\nroute = \"/users/...\"\n\n[[component]]\nid = \"admin\"\n[component.trigger]\nroute = \"/users/admin\"\n\n[[component]]\nid = \"shop\"\n[component.trigger]\nroute = \"/...\"","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#resolving-overlapping-routes"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Health Check Route","content":"Every HTTP application automatically has a special route always configured at /.well-known/spin/health, which\nreturns OK 200 when the Spin instance is healthy.","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#health-check-route"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"HTTP Components","content":"undefinedBy default, Spin runs components using the undefined. In this model, the Wasm module exports a well-known function that Spin calls to handle the HTTP request.","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#http-components"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"The Request Handler","content":"The exact signature of the HTTP handler, and how a function is identified to be exported as the handler, will depend on your language.{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}In Rust, the handler is identified by the #[spin_sdk::http_component] attribute. It takes a spin_sdk::http::Request, and returns a spin_sdk::http::Response (or error). These types are instantiations of the standard http::Request and http::Response types and behave exactly like them:use anyhow::Result;\nuse spin_sdk::{\n http::{Request, Response},\n http_component,\n};\n\n/// A simple Spin HTTP component.\n#[http_component]\nfn handle_hello_rust(req: Request) -> Result {\n Ok(http::Response::builder()\n .status(200)\n .header(\"foo\", \"bar\")\n .body(Some(\"Hello, Fermyon\".into()))?)\n}{{ blockEnd }}{{ startTab \"TypeScript\"}}In JavaScript or TypeScript, the handler is identified by name. It must be called handleRequest. The way you declare it is slightly different between the two languages.In undefined, handleRequest is declared as export async function. It takes a JsvaScript object representing the request, and returns a response object. The fields of these objects are exactly the same as in TypeScript:export async function handleRequest(request) {\n return {\n status: 200,\n headers: { \"foo\": \"bar\" },\n body: \"Hello from JS-SDK\"\n }\n}In undefined, handleRequest is declared as an export const of the HandleRequest function type - that is, a function literal rather than a function declaration. It takes a HttpRequest object, and returns a HttpResponse object, both defined in the @fermyon/spin-sdk package:import { HandleRequest, HttpRequest, HttpResponse} from \"@fermyon/spin-sdk\"\n\nexport const handleRequest: HandleRequest = async function(request: HttpRequest): Promise {\n return {\n status: 200,\n headers: { \"foo\": \"bar\" },\n body: \"Hello from TS-SDK\"\n }\n}{{ blockEnd }}{{ startTab \"Python\"}}In Python, the handler is identified by name. It must be called handle_request. It takes a request object and must return an instance of Response, defined in the spin_http package:from spin_http import Response\n\ndef handle_request(request):\n return Response(200,\n [(\"content-type\", \"text/plain\")],\n bytes(f\"Hello from the Python SDK\", \"utf-8\")){{ blockEnd }}{{ startTab \"TinyGo\"}}In Go, you register the handler as a callback in your program's init function. Call spinhttp.Handle, passing your handler as the sole argument. Your handler takes a http.Request record, from the standard net/http package, and a ResponseWriter to construct the response.undefinedpackage main\n\nimport (\n \"fmt\"\n \"net/http\"\n\n spinhttp \"github.com/fermyon/spin/sdk/go/http\"\n)\n\nfunc init() {\n spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {\n w.Header().Set(\"Content-Type\", \"text/plain\")\n fmt.Fprintln(w, \"Hello Fermyon!\")\n })\n}\n\nfunc main() {}undefined{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#the-request-handler"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"The Request and Response Records","content":"Exactly how the Spin SDK surfaces the request and response types varies from language to language; this section calls out general features.undefined","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#the-request-and-response-records"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Additional Request Information","content":"As well as any headers passed by the client, Spin sets several headers on the request passed to your component, which you can use to access additional information about the HTTP request.undefinedundefined| Header Name | Value | Example |\n|------------------------------|----------------------|---------|\n| spin-full-url | The full URL of the request. This includes full host and scheme information. | https://example.com:8080/shop/users/1/edit?theme=pink |\n| spin-path-info | The request path relative to the component route (including any base) | /1/edit |\n| spin-matched-route | The part of the request path that was matched by the route (including the base and wildcard indicator if present) | /shop/users/... |\n| spin-raw-component-route | The component route pattern matched, as written in the component manifest (that is, undefined the base, but including the wildcard indicator if present) | /users/... |\n| spin-component-route | The component route pattern matched, undefined any wildcard indicator | /users |\n| spin-base-path | The application base path | /shop |","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#additional-request-information"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Inside HTTP Components","content":"For the most part, you'll build HTTP component modules using a language SDK (see the Language Guides section), such as a JavaScript module or a Rust crate. If you're interested in what happens inside the SDK, or want to implement HTTP components in another language, read on!undefinedThe HTTP component interface is defined using a WebAssembly Interface (WIT) file. (undefined). You can find the latest WITs for Spin HTTP components at undefined.The core HTTP types are defined in undefined:// wit/ephemeral/http-types.wit\n\n// The HTTP status code.\ntype http-status = u16\n// The HTTP body.\ntype body = list\n// The HTTP headers represented as a list of (name, value) pairs.\ntype headers = list>\n// The HTTP parameter queries, represented as a list of (name, value) pairs.\ntype params = list>\n// The HTTP URI of the current request.\ntype uri = string\n// The HTTP method.\nenum method { get, post, put,... }\n\n// An HTTP request.\nrecord request {\n method: method,\n uri: uri,\n headers: headers,\n params: params, // Retained for binary compatibility but no longer used\n body: option,\n}\n\n// An HTTP response.\nrecord response {\n status: http-status,\n headers: option,\n body: option,\n}\n\n// error types omittedundefinedThe entry point for Spin HTTP components is then defined in undefined:// wit/ephemeral/spin-http.wit\n\nuse * from http-types\n\n// The entry point for an HTTP handler.\nhandle-http-request: function(req: request) -> responseThis is the function signature that all HTTP components must implement, and\nwhich is used by the Spin HTTP executor when instantiating and invoking the\ncomponent.This interface (spin-http.wit) can be directly used together with the\nundefined\nto build a component that the Spin HTTP executor can invoke.This is exactly how Spin SDKs, such as the undefined, undefined, undefined and undefined SDKs, are built.\nAs more languages add support for the component model, we plan to add support for them in the same way.undefined","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#inside-http-components"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"HTTP With Wagi (WebAssembly Gateway Interface)","content":"The WebAssembly component model proposal is currently in its early stages, which\nmeans only a few programming languages fully implement it. While the language\ncommunities implement toolchain support for the component model (for emitting\ncomponents and for automatically generating bindings for importing other\ncomponents), we want to allow developers to use any language that compiles to\nWASI to build Spin HTTP applications. This is why Spin currently implements an\nHTTP executor based on undefined, or the\nWebAssembly Gateway Interface, a project that implements the\nundefined\nspecification for WebAssembly.undefinedWagi allows a module built in any programming language that compiles to undefined\nto handle an HTTP request by passing the HTTP request information to the module's\nstandard input, environment variables, and arguments, and expecting the HTTP\nresponses through the module's standard output.\nThis means that if a language has support for the WebAssembly System Interface,\nit can be used to build Spin HTTP components.\nThe Wagi model is only used to parse the HTTP request and response. Everything\nelse — defining the application, running it, or undefined\nis done the same way as a component that uses the Spin executor.","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#http-with-wagi-webassembly-gateway-interface"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Wagi Component Requirements","content":"Spin uses the component model by default, and cannot detect from the Wasm module alone whether it was built with component model support. For Wagi components, therefore, you must tell Spin in the component manifest to run them using Wagi instead of 'default' Spin. To do this, use the executor field in the [component.trigger] table:[[component]]\nid = \"wagi-test\"\nsource = \"wagitest.wasm\"\n[component.trigger]\nroute = \"/\"\nexecutor = { type = \"wagi\" }undefinedWagi supports non-default entry points, and allows you to pass an arguments string that a program can receive as if it had been passed on the command line. If you need these you can specify them in the executor table. For details, see the undefined.","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#wagi-component-requirements"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Request Handling in Wagi","content":"Building a Wagi component in a particular programming language that can compile\nto wasm32-wasi does not require any special libraries — instead,\nundefined can\nbe done by reading the HTTP request from the standard input and environment\nvariables, and sending the HTTP response to the module's standard output.In pseudo-code, this is the minimum required in a Wagi component:undefinedundefinedundefinedprint(\"content-type: text/html; charset=UTF-8\\n\\n\");\nprint(\"hello world\\n\");Here is a working example, written in undefined,\na programming language that natively targets WebAssembly and WASI but\ndoes not yet support the component model:import Process from \"sys/process\";\nimport Array from \"array\";\n\nprint(\"content-type: text/plain\\n\");\n\n// This will print all the Wagi env variables\nprint(\"==== Environment: ====\");\nArray.forEach(print, Process.env());\n\n// This will print the route path followed by each query\n// param. So /foo?bar=baz will be [\"/foo\", \"bar=baz\"].\nprint(\"==== Args: ====\");\nArray.forEach(print, Process.argv());undefined","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#request-handling-in-wagi"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Wagi HTTP Environment Variables","content":"Wagi passes request metadata to the program through well-known environment variables. The key path-related request variables are:undefinedundefinedundefinedundefinedundefinedundefinedFor details, and for a full list of all Wagi environment variables, see\nundefined.","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#wagi-http-environment-variables"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Exposing HTTP Triggers Using HTTPS","content":"When exposing HTTP triggers using HTTPS you must provide spin up with a TLS certificate and a private key. This can be achieved by either using trigger options (--tls-cert and --tls-key) when running the spin up command, or by setting environment variables (SPIN_TLS_CERT and SPIN_TLS_KEY) before running the spin up command.","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#exposing-http-triggers-using-https"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Trigger Options","content":"The spin up command's --tls-cert and --tls-key trigger options provide a way for you to specify both a TLS certificate and a private key (whilst running the spin up command).The --tls-cert option specifies the path to the TLS certificate to use for HTTPS, if this is not set, normal HTTP will be used. The certificate should be in PEM format.The --tls-key option specifies the path to the private key to use for HTTPS, if this is not set, normal HTTP will be used. The key should be in PKCS#8 format. For more information, please see the undefined.","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#trigger-options"},{"project":"spin","title":"The Spin HTTP Trigger","subheading":"Environment Variables","content":"The spin up command can also automatically use the SPIN_TLS_CERT and SPIN_TLS_KEY environment variables instead of the respective flags (--tls-cert and --tls-key):SPIN_TLS_CERT=\nSPIN_TLS_KEY=Once set, spin up will automatically use these explicitly set environment variables. For example, if using a Linux-based system, you can go ahead and use the export command to set the variables in your session (before you run the spin up command):export SPIN_TLS_CERT=\nexport SPIN_TLS_KEY=","keywords":"","subsectionKeywords":"","url":"/spin/http-trigger.md#environment-variables"},{"project":"spin","title":"Introducing Spin","subheading":"","content":"Spin is a framework for building and running event-driven microservice applications with WebAssembly (Wasm) components.Spin uses Wasm because it is undefined. Millisecond cold start times mean no need to keep applications \"warm\".Many languages have Wasm implementations, so undefined.Spin is undefined and undefined, meaning you can take your Spin applications anywhere. There are Spin implementations for local development, for self-hosted servers, for Kubernetes, and for cloud-hosted services.undefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/spin/index"},{"project":"spin","title":"Install Spin","subheading":"","content":"undefinedundefinedundefinedundefinedundefined","keywords":"install","subsectionKeywords":"","url":"/spin/install"},{"project":"spin","title":"Install Spin","subheading":"Installing Spin","content":"Spin runs on Linux (amd64 and arm64), macOS (Intel and Apple Silicon), and Windows (amd64).{{ tabs \"os\" }}{{ startTab \"Linux\"}}There are multiple ways to install Spin. The easiest is to use the installer script, hosted on this site:It's highly recommended to add Spin to a folder, which is on your path, e.g.:$ sudo mv spin /usr/local/bin/To install a specific version, you can pass arguments to the install script this way:To install the canary version of spin, you should pass the argument -v canary. The canary version is always the latest commit to the main branch of Spin:{{ blockEnd }}{{ startTab \"macOS\"}}There are multiple ways to install Spin. The easiest is to use the installer script, hosted on this site:It's highly recommended to add Spin to a folder, which is on your path, e.g.:$ sudo mv spin /usr/local/bin/To install a specific version, you can pass arguments to the install script this way:To install the canary version of spin, you should pass the argument -v canary. The canary version is always the latest commit to the main branch of Spin:{{ blockEnd }}{{ startTab \"Windows\"}}If using Windows (PowerShell / cmd.exe), you can download the Windows binary release of Spin.Simply unzip the binary release and place the spin.exe in your system path.If you want to use WSL2 (Windows Subsystem for Linux 2), please follow the instructions for using Linux.{{ blockEnd }}\n{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/install.md#installing-spin"},{"project":"spin","title":"Install Spin","subheading":"Verifying the Release Signature","content":"The Spin project undefined using undefined, a project that helps with signing software and undefined. Consumers of Spin releases can validate the integrity of the package they downloaded by performing a validation of the artifact against the signature present in the public log. Specifically, users get two main guarantees by verifying the signature: 1) that the author of the artifact is indeed the one expected (i.e. the build infrastructure associated with the Spin project, at a given revision that can be inspected), and 2) that the content generated by the build infrastructure has not been tampered with.To verify the release signature, first undefined. This is the CLI tool that we will use validate the signature.\nThe same directory where the installation script was run should also contain a signature of the Spin binary and the certificate used to perform the signature. The following command will perform the signature verification using the cosign CLI:You can now move the Spin binary to the path knowing that it was indeed built by the infrastructure associated with the Spin project, and that it has not been tampered with since the build.","keywords":"","subsectionKeywords":"","url":"/spin/install.md#verifying-the-release-signature"},{"project":"spin","title":"Install Spin","subheading":"Building Spin From Source","content":"undefined for a detailed guide on building Spin from source:undefined$ sudo apt-get install build-essential libssl-dev pkg-config","keywords":"","subsectionKeywords":"","url":"/spin/install.md#building-spin-from-source"},{"project":"spin","title":"Install Spin","subheading":"Using Cargo to Install Spin","content":"If you have undefined, you can clone the repo and install it to your path:undefined$ rustup update","keywords":"","subsectionKeywords":"","url":"/spin/install.md#using-cargo-to-install-spin"},{"project":"spin","title":"Install Spin","subheading":"Next Steps","content":"undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/install.md#next-steps"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedWith JavaScript being a very popular language, Spin provides support for building components with it using the experimental SDK. The development of the JavaScript SDK is continually being worked on to improve user experience and add features.undefinedundefinedundefinedIn order to compile JavaScript programs to Spin components, you also need to install a Spin plugin js2wasm using the following command:$ spin plugin update\n$ spin plugin install js2wasm","subsectionKeywords":"","url":"/spin/javascript-components"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"Installing Templates","content":"The JavaScript/TypeScript templates can be installed from undefined using the following command:$ spin templates install --git https://github.com/fermyon/spin-js-sdk --updatewhich will install the http-js and http-ts templates:Copying remote template source\nInstalling template http-ts...\nInstalling template http-js...\nInstalled 2 template(s)\n\n+-------------------------------------------------+\n| Name Description |\n+=================================================+\n| http-js HTTP request handler using Javascript |\n| http-ts HTTP request handler using Typescript |\n+-------------------------------------------------+","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#installing-templates"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"Structure of a JS/TS Component","content":"A new JS/TS component can be created using the following command:$ spin new http-ts hello-world --accept-defaultsThis creates a directory of the following structure:hello-world/\n├── package.json\n├── package-lock.json\n├── README.md\n├── spin.toml\n├── src\n│   └── index.ts\n├── tsconfig.json\n└── webpack.config.jsThe source for the component is present in src/index.ts. undefined is used to bundle the component into a single .js file which will then be compiled to a .wasm module using the js2wasm plugin.","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#structure-of-a-jsts-component"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"Building and Running the Template","content":"First, the dependencies for the template need to be installed and then bundled into a single JavaScript file using the following commands:$ cd hello-world\n$ npm install\n$ npm run buildOnce a Spin compatible module is created, it can be run using:$ spin up","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#building-and-running-the-template"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"A Quick Note About NPM Scripts","content":"Please note that using pre-built NPM scripts can have different effects on different Operating Systems (OSs). Let's take the npm run build command (like undefined) as an example:\"scripts\": {\n \"build\": \"npx webpack --mode=production && mkdir -p target && spin js2wasm -o target/spin-http-js.wasm dist/spin.js\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n }The npm run build command will work on Linux and macOS. However, on Windows it will create both a -p directory and a target directory.On Linux/Unix systems, the -p option in the mkdir command is designed to prevent an error from occuring in the event that the target directory already exists. However, on Windows systems, npm (by default) uses cmd.exe which does not recognize the -p option, regarding its mkdir command.If you run npm run build on Windows (more than once) the following error will be encountered:A subdirectory or file -p already exists\nA Subdirectory or file target already existsIf any errors, as described above, occur please consider one of the two following options:a) Configure your instance of npm to use bash (by using the script-shell configuration setting):$ npm config set script-shell \"C:\\\\Program Files\\\\git\\\\bin\\\\bash.exe\"b) Run the separate parts of the build manually, to suite your needs (OS syntax requirements):$ npx webpack --mode=production\n$ spin js2wasm -o target/spin-http-js.wasm dist/spin.js","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#a-quick-note-about-npm-scripts"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"HTTP Components","content":"In Spin, HTTP components are triggered by the occurrence of an HTTP request, and\nmust return an HTTP response at the end of their execution. Components can be\nbuilt in any language that compiles to WASI, and Javascript/TypeScript has improved support\nfor writing Spin components with the Spin JS/TS SDK.undefinedBuilding a Spin HTTP component using the JS/TS SDK means writing a single function\nthat takes an HTTP request as a parameter, and returns an HTTP response — below\nis a complete implementation for such a component in TypeScript:import { HandleRequest, HttpRequest, HttpResponse } from \"@fermyon/spin-sdk\"\n\nconst encoder = new TextEncoder()\n\nexport const handleRequest: HandleRequest = async function (request: HttpRequest): Promise {\n\n return {\n status: 200,\n headers: {\"foo\": \"bar\"},\n body: encoder.encode(\"Hello from JS-SDK\").buffer\n }\n}The important things to note in the implementation above:undefinedundefinedPlease note: If you need to decode a request body (which is either an ArrayBuffer or ArrayBufferView) into plain text or JSON please consider using the following:// Create new TextDecoder instance\nlet decoder = new TextDecoder()\n\n// Then decode request body to text\nlet text = decoder.decode(request.body)\n\n// Or decode request body to JSON\nlet text = JSON.parse(decoder.decode(request.body))","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#http-components"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"Sending Outbound HTTP Requests","content":"If allowed, Spin components can send outbound HTTP requests.\nLet's see an example of a component that makes a request to\nundefined and\ninserts a custom header into the response before returning:import { HandleRequest, HttpRequest, HttpResponse } from \"@fermyon/spin-sdk\"\n\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder()\n\nexport const handleRequest: HandleRequest = async function (request: HttpRequest): Promise {\n\n const dogFact = await fetch(\"https://some-random-api.ml/facts/dog\")\n\n const dogFactBody = await dogFact.text()\n\n const env = JSON.stringify(process.env)\n\n const body = `Here's a dog fact: ${dogFactBody}\\n`\n\n return {\n status: 200,\n headers: { \"foo\": \"bar\" },\n body: encoder.encode(body).buffer\n }\n}Before we can execute this component, we need to add the some-random-api.ml\ndomain to the application manifest allowed_http_hosts list containing the list of\ndomains the component is allowed to make HTTP requests to:# spin.toml\nspin_manifest_version = \"1\"\nauthors = [\"Fermyon Engineering \"]\nname = \"spin-http-js\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"1.0.0\"\n\n[variables]\nobject = { default = \"teapot\" }\n\n[[component]]\nid = \"hello\"\nsource = \"target/spin-http-js.wasm\"\nallowed_http_hosts = [\"https://some-random-api.ml\"]\n[component.trigger]\nroute = \"/hello\"\n[component.build]\ncommand = \"npm run build\"The component can be built using the spin build command. Running the application using spin up --file spin.toml will start the HTTP\nlistener locally (by default on localhost:3000), and our component can\nnow receive requests in route /hello:$ curl -i localhost:3000/hello\nHTTP/1.1 200 OK\ndate: Fri, 18 Mar 2022 03:54:36 GMT\ncontent-type: application/json; charset=utf-8\ncontent-length: 185\nserver: spin/0.1.0\n\nHere's a dog fact: {\"fact\":\"It's a myth that dogs only see in black and white. In fact, it's believed that dogs see primarily in blue, greenish-yellow, yellow and various shades of gray.\"}undefinedundefinedWe just built a WebAssembly component that sends an HTTP request to another\nservice, manipulates that result, then responds to the original request.\nThis can be the basis for building components that communicate with external\ndatabases or storage accounts, or even more specialized components like HTTP\nproxies or URL shorteners.","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#sending-outbound-http-requests"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"Storing Data in Redis From JS/TS Components","content":"undefinedUsing the Spin's JS SDK, you can use the Redis key/value store and to publish messages to Redis channels.Let's see how we can use the JS/TS SDK to connect to Redis:import { HandleRequest, HttpRequest, HttpResponse } from \"@fermyon/spin-sdk\"\n\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder()\n\nconst redisAddress = \"redis://localhost:6379/\"\n\nexport const handleRequest: HandleRequest = async function (request: HttpRequest): Promise {\n\n spinSdk.redis.incr(redisAddress, \"test\")\n spinSdk.redis.incr(redisAddress, \"test\")\n\n console.log(decoder.decode(spinSdk.redis.get(redisAddress, \"test\")))\n\n spinSdk.redis.set(redisAddress, \"test-set\", encoder.encode(\"This is a test\").buffer)\n\n console.log(decoder.decode(spinSdk.redis.get(redisAddress, \"test-set\")))\n\n spinSdk.redis.publish(redisAddress, \"test\", encoder.encode(\"This is a test\").buffer)\n\n return {\n status: 200,\n headers: {\"foo\": \"bar\"},\n body: encoder.encode(\"Hello from JS-SDK\").buffer\n }\n}This HTTP component demonstrates fetching a value from Redis by key, setting a key with a value, and publishing a message to a Redis channel. The component is triggered by an HTTP request served on the route configured in the spin.toml:undefined","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#storing-data-in-redis-from-jsts-components"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"Routing in a Component","content":"The JavaScript/TypeScript SDK provides a router that makes it easier to handle routing within a component. The router is based on undefined. An additional function handleRequest has been implemented in the router to allow passing in the Spin HTTP request directly. For a more complete documentation on the route, checkout the documentationa at undefined. An example usage of the router is given below:import { HandleRequest, HttpRequest, HttpResponse} from \"@fermyon/spin-sdk\"\n\nlet router = utils.Router()\n\nfunction handleDefaultRoute() {\n return {\n status: 200,\n headers: { \"content-type\": \"text/html\" },\n body: \"Hello from Default Route\"\n }\n}\n\nfunction handleHomeRoute(id: string) {\n return {\n status: 200,\n headers: { \"content-type\": \"text/html\" },\n body: \"Hello from Home Route with id:\" + id\n }\n}\n\nrouter.get(\"/\", handleDefaultRoute)\nrouter.get(\"/home/:id\", ({params}) => handleHomeRoute(params.id))\n\nexport const handleRequest: HandleRequest = async function(request: HttpRequest): Promise {\n return await router.handleRequest(request)\n}","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#routing-in-a-component"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"Using External NPM Libraries","content":"undefinedSome NPM packages can be installed and used in the component. If a popular library does not work, please open an issue/feature request in the undefined.","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#using-external-npm-libraries"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"Suggested Libraries for Common Tasks","content":"These are some of the suggested libraries that have been tested and confired to work with the SDK for common tasks.{{ details \"HTML parsers\" \"- undefined\" }}{{ details \"Parsing formdata\" \"- undefined\" }}{{ details \"Runtime schema validation\" \"- undefined\" }}{{ details \"Unique ID generator\" \"- undefined\\n- undefined\\n- undefined\" }}","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#suggested-libraries-for-common-tasks"},{"project":"spin","title":"Building Spin Components in JavaScript","subheading":"Caveats","content":"undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/javascript-components.md#caveats"},{"project":"spin","title":"Spin on Kubernetes","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/spin/kubernetes"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Why Use Spin With Kubernetes?","content":"In addition to spin up Fermyon also offers Fermyon Cloud to deploy spin apps into production, so why use Spin with Kubernetes? For users that have made existing investments into Kubernetes or have requirements that their applications stay within certain clouds, not be on shared infrastructure, or run on-premise, Kubernetes provides a robust solution.","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#why-use-spin-with-kubernetes"},{"project":"spin","title":"Spin on Kubernetes","subheading":"How Does It Work?","content":"For Kubernetes to run Spin workloads, it needs to be taught about a new runtime class. To do this, there is a shim for containerd. This compiles to a binary that must be placed on the Kubernetes nodes that host Shim pods. That binary then needs to be registered with Kubernetes as a new RuntimeClass. After that, wasm containers can be deployed to Kubernetes using the Spin k8s plugin.","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#how-does-it-work"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Next Steps","content":"undefinedundefined{{ tabs \"platforms\" }}{{ startTab \"Azure AKS\"}}","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#next-steps"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Setup Azure AKS for Spin","content":"","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#setup-azure-aks-for-spin"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Introduction","content":"Azure AKS provides a straightforward and officially undefined way to use Spin with AKS.","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#introduction"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Known Limitations","content":"undefinedundefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#known-limitations"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Setup","content":"To get spin working on an AKS cluster, a few setup steps are required. First Add the aks-preview extension$ az extension add --name aks-previewNext update to the latest version:$ az extension update --name aks-previewRegister the WasmNodePoolPreview feature$ az feature register --namespace \"Microsoft.ContainerService\" --name \"WasmNodePoolPreview\"This will take a few minutes to complete. You can verify it’s done when this command returns undefined$ az feature show --namespace \"Microsoft.ContainerService\" --name \"WasmNodePoolPreview\"Finally refresh the registration of the ContainerService$ az provider register --namespace Microsoft.ContainerServiceOnce the service is registered, the next step is to add a Wasm/WASI nodepool to an existing AKS cluster. If a cluster doesn’t already exist, follow Azure’s undefined to create a new cluster.$ az aks nodepool add \\\n --resource-group myResourceGroup \\\n --cluster-name myAKSCluster \\\n --name mywasipool \\\n --node-count 1 \\\n --workload-runtime WasmWasiYou can verify the workloadRuntime using the following command$ az aks nodepool show -g myResourceGroup --cluster-name myAKSCluster -n mywasipool --query workloadRuntimeThe next set of commands uses kubectl to create the necessary runtimeClass. If you don’t already have kubectl configured with the appropriate credentials, you can retrieve them with this command$ az aks get-credentials -n myakscluster -g myresourcegroupFind the name of the nodepool$ kubectl get nodes -o wideThen retrieve detailed information on the appropriate nodepool and verify among it’s labels is “kubernetes.azure.com/wasmtime-spin-v1=true”kubectl describe node aks-mywasipool-12456878-vmss000000Create a file wasm-runtimeclass.yml and populate with the following informationapiVersion: node.k8s.io/v1\nkind: RuntimeClass\nmetadata:\n name: \"wasmtime-spin\"\nhandler: \"spin\"\nscheduling:\n nodeSelector:\n \"kubernetes.azure.com/wasmtime-spin\": \"true\"Then register the runtime class with the cluster$ kubectl apply -f wasm-runtimeclass.yaml{{ blockEnd }}{{ startTab \"Docker Desktop\"}}","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#setup"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Setup Docker Desktop for Spin","content":"","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#setup-docker-desktop-for-spin"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Introduction","content":"Docker Desktop provides both an easy way to run Spin apps in containers directly and it's own Kuberentes option.","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#introduction"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Known Limitations","content":"undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#known-limitations"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Setup","content":"Install approproiate Preview Version of Docker Desktop+Wasm Technical Preview 2 from undefined. Then undefined for Docker in Settings → Experimental → Use containerd for pulling and storing images.Next Enable Kubernetes under Settings → Experimental → Enable Kubernetes, then hit “Apply & Restart”Create a file wasm-runtimeclass.yml and populate with the following informationapiVersion: node.k8s.io/v1\nkind: RuntimeClass\nmetadata:\n name: \"wasmtime-spin\"\nhandler: \"spin\"Then register the runtime class with the cluster$ kubectl apply -f wasm-runtimeclass.yaml","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#setup"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Using Docker Desktop With Spin","content":"Docker Desktop can be used with spin as a Kubernetes target per the instructions in the below in this document. However Docker can also run the containers directly with the following command:$ docker run --runtime=io.containerd.spin.v1 --platform=wasi/wasm -p : :If there is not command specified in the Dockerfile, one will need to be passed at the command line. Since Spin doesn't need this, \"/\" can be passed.{{ blockEnd }}{{ startTab \"K3d\"}}","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#using-docker-desktop-with-spin"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Setup K8s for Spin","content":"","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#setup-k8s-for-spin"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Introduction","content":"undefined is a lightweight Kubernetes installation.","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#introduction"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Known Limitations","content":"undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#known-limitations"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Setup","content":"Ensure both Docker and k3d are installed. Then undefined for Docker in Settings → Experimental → Use containerd for pulling and storing images.Deis Labs provides a preconfigured K3d environment that can be run using this command$ k3d cluster create wasm-cluster --image ghcr.io/deislabs/containerd-wasm-shims/examples/k3d:v0.3.3 -p \"8081:80@loadbalancer\" --agents 2 --registry-create mycluster-registry:12345Create a file wasm-runtimeclass.yml and populate with the following informationapiVersion: node.k8s.io/v1\nkind: RuntimeClass\nmetadata:\n name: \"wasmtime-spin\"\nhandler: \"spin\"Then register the runtime class with the cluster$ kubectl apply -f wasm-runtimeclass.yaml{{ blockEnd }}{{ startTab \"Generic Kubernetes\"}}","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#setup"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Setup Generic Kubernetes for Spin","content":"","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#setup-generic-kubernetes-for-spin"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Introduction","content":"These instructions are provided for a self-managed or other Kubernetes service that isn't documented elsewhere.","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#introduction"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Known Limitations","content":"undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#known-limitations"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Setup","content":"Clone containerd shim repository. Cd into the directory and run make$ git clone [https://github.com/deislabs/containerd-wasm-shims](https://github.com/deislabs/containerd-wasm-shims)\n$ cd containerd-wasm-shims\n$ makeCopy the containerd-shim-spin-v1 to the /bin directory of kubernetes node image. Then add the following lines to the config.toml for containerd. [plugins.cri.containerd.runtimes.spin]\n runtime_type = \"io.containerd.spin.v1\"Create a file wasm-runtimeclass.yml and populate with the following informationapiVersion: node.k8s.io/v1\nkind: RuntimeClass\nmetadata:\n name: \"wasmtime-spin\"\nhandler: \"spin\"Then register the runtime class with the cluster$ kubectl apply -f wasm-runtimeclass.yaml{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#setup"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Run a Spin Workload on Kubernetes","content":"","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#run-a-spin-workload-on-kubernetes"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Introduction","content":"This guide demonstrates the commands to run a Spin workload in Kubernetes. It should apply to all Kubernetes varients which have been properly configured using one of the Setup guides.","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#introduction"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Concepts","content":"Spin apps are bundled using either Bindle or OCI (as of Spin 0.8). These packages include the spin.toml file, and the wasm and static files which it references. While Kubernetes also uses OCI repositories, it expects the package to be in a container format.The containerd shim for Spin allows Kubernetes to appropriately schedule Spin applications as long as they have been wrapped in a lightweight “scratch” container.Once the Spin App Container has been created, it can be pushed to an OCI repository, and then deployed to an appropriately configured kubernetes cluster.","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#concepts"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Requirements","content":"The current Spin k8s plugin relies on Docker and Kubectl under the hood. It’s important that both of these tools be installed, the Docker service be running, and KUBECONFIG environment variable be configured to point to a kubeconfig file for the desired Kubernetes cluster.","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#requirements"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Install Plugin","content":"To install this plugin, simply run:spin plugin install -u https://raw.githubusercontent.com/chrismatteson/spin-plugin-k8s/main/k8s.json","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#install-plugin"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Workflow","content":"The workflow is very similar to the workflow for Fermyon Cloud.The k8s plugin handles all of the tasks necessary to build the docker container, push it to a repository and deploy it into production.Just like with Fermyon Cloud, when something changes with the application, the workflow is to iterate the version in the spin.toml file, and restart the sequence from spin build.","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#workflow"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Detailed Explanation of Steps","content":"","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#detailed-explanation-of-steps"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Spin New","content":"An optional command to use a template to create a new Spin App:$ spin new","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#spin-new"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Spin Build","content":"The following command builds a spin app:spin build","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#spin-build"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Spin K8s Scaffold","content":"The following command creates two files necessary for a Spin app to run on Kubernetes. A Dockerfile and deploy.yaml. Scaffold takes in a namespace as a mandatory argument. This can either be a username if using the Docker hub, or can be the entire address if using a separate repository such as ghcr:spin k8s scaffoldAn example Dockerfile is below. The only things which end up in the final image are wasm and other files mentioned as sources in the spin.toml.DockerfileFROM scratch AS build\nWORKDIR /tmp/test\nCOPY . .\n\nFROM scratch\nCOPY --from=build /tmp/test/spin.toml .\nCOPY --from=build /tmp/test/target/wasm32-wasi/release/test.wasm ./target/wasm32-wasi/release/test.wasm\nCOPY --from=build /tmp/test/spin_static_fs.wasm ./spin_static_fs.wasm\nCOPY --from=build /tmp/test/static ./staticThe deploy.yaml file defines the deployment to the Kubernetes server. By default the replicas is configured as 3, however this can be edited after the scaffolding stage and before the deployment stage. Additionally, the runtimeClassName is defined as wasmtime-spin-v1. It’s critical that that is the exact name used when setting up the Kubernetes service to support Spin, or that the deployment be edited to the appropriate name.deploy.yamlapiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: test\nspec:\n replicas: 3\n selector:\n matchLabels:\n app: test\n template:\n metadata:\n labels:\n app: test\n spec:\n runtimeClassName: wasmtime-spin\n containers:\n - name: test\n image: chrismatteson/test:0.1.5\n command: [\"/\"]\n---\napiVersion: v1\nkind: Service\nmetadata:\n name: test\n spec:\n type: LoadBalancer\n ports:\n - protocol: TCP\n port: 80\n targetPort: 80\n selector:\n app: test\n---\napiVersion: [networking.k8s.io/v1](http://networking.k8s.io/v1)\nkind: Ingress\nmetadata:\n name: test\n annotations:\n [ingress.kubernetes.io/ssl-redirect:](http://ingress.kubernetes.io/ssl-redirect:) \"false\"\n [kubernetes.io/ingress.class:](http://kubernetes.io/ingress.class:) traefik\nspec:\n rules:\n - http:\n paths:\n - path: /\n pathType: Prefix\n backend:\n service:\n name: test\n port:\n number: 80","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#spin-k8s-scaffold"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Spin K8s Build","content":"The following command uses the Dockerfile to locally build a Spin Docker Container. The container is tagged as latest and with the version from the spin.toml:$ spin k8s build","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#spin-k8s-build"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Spin K8s Push","content":"The following command pushes the Dockerfile to the appropriate repository:$ spin k8s push","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#spin-k8s-push"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Spin K8s Deploy","content":"The following command deploys the application to Kubernetes:$ spin k8s deploy","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#spin-k8s-deploy"},{"project":"spin","title":"Spin on Kubernetes","subheading":"Spin K8s Getsvc","content":"The following command retrieves information about the service that gets deployed (such as it’s external IP):$ spin k8s getsvc\n``","keywords":"","subsectionKeywords":"","url":"/spin/kubernetes.md#spin-k8s-getsvc"},{"project":"spin","title":"Key Value Store","subheading":"","content":"undefinedundefinedundefinedSpin provides an interface for you to persist data in a key value store managed by Spin. This key value store allows Spin developers to persist non-relational data across application invocations. To learn more about key value store use cases and how to enable your Spin application to use a key value store, check out our undefined.{{ details \"Why do I need a Spin interface? Why can't I just use my own external store?\" \"You can absolutely still use your own external store either with the Redis or Postgres APIs, or outbound HTTP. However, if you're interested in quick, non-relational local storage without any infrastructure set-up then Spin's key value store is a great option.\" }}","subsectionKeywords":"","url":"/spin/kv-store-api-guide"},{"project":"spin","title":"Key Value Store","subheading":"Using Key Value Store From Applications","content":"The Spin SDK surfaces the Spin key value store interface to your language. The following characteristics are true of keys and values:undefinedundefinedundefinedThe set of operations is common across all SDKs:| Operation | Parameters | Returns | Behavior |\n|------------|------------|---------|----------|\n| open | name | store | Open the store with the specified name. If name is the string \"default\", the default store is opened for the component that was granted access in the component manifest from spin.toml. Otherwise, name must refer to a store defined and configured in a undefined supplied with the application.|\n| get | store, key | key | Get the value associated with the specified key from the specified store. |\n| set | store, key, value | - | Set the value associated with the specified key in the specified store, overwriting any existing value. |\n| delete | store, key | - | Delete the tuple with the specified key from the specified store. error::invalid-store will be raised if store is not a valid handle to an open store. No error is raised if a tuple did not previously exist for key.|\n| exists | store, key | boolean | Return whether a tuple exists for the specified key in the specified store.|\n| get-keys | store | list | Return a list of all the keys in the specified store. |\n| close | store | - | Close the specified store. |The exact detail of calling these operations from your application depends on your language:{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}Key value functions are available in the spin_sdk::key_value module. The function names match the operations above. For example:use anyhow::Result;\nuse spin_sdk::{\n http::{Request, Response},\n http_component,\n key_value::{Error, Store},\n};\n#[http_component]\nfn handle_request(req: Request) -> Result {\n let store = Store::open_default()?;\n store.set(\"mykey\", \"myvalue\")?;\n let value = store.get(\"mykey\")?;\n Ok(http::Response::builder().status(200).body(Some(value.into()))?)\n}undefined\nset undefinedundefinedget undefinedundefinedopen undefinedundefined{{ blockEnd }}{{ startTab \"Typescript\"}}With Typescript, the key value functions can be accessed after opening a store using either the spinSdk.kv.open or the spinSdk.kv.openDefault methods which returns a handle to the store. For example:import { HandleRequest, HttpRequest, HttpResponse } from \"@fermyon/spin-sdk\"\n\nexport const handleRequest: HandleRequest = async function (request: HttpRequest): Promise {\n let store = spinSdk.kv.openDefault()\n store.set(\"mykey\", \"myvalue\")\n return {\n status: 200,\n headers: {\"content-type\":\"text/plain\"},\n body: store.get(\"mykey\")\n }\n}\nundefinedundefinedset undefinedundefined{{ blockEnd }}{{ startTab \"Python\"}}The key value functions are provided through the spin_key_value module in the Python SDK. For example:from spin_http import Response\nfrom spin_key_value import kv_open_default\n\n\ndef handle_request(request):\n\n store = kv_open_default()\n store.set(\"mykey\", \"myvalue\")\n value = store.get()\n //\n return Response(status, [(\"content-type\", \"text/plain\")], value) \nundefinedget undefinedundefined{{ blockEnd }}{{ startTab \"TinyGo\"}}Key value functions are provided by the github.com/fermyon/spin/sdk/go/key_value module. Here is the reference undefined. For example:import \"github.com/fermyon/spin/sdk/go/key_value\"\n\nfunc example() error {\n store, err := key_value.Open(\"default\")\n if err != nil {\n return err\n }\n defer key_value.Close(store)\n return key_value.Set(store, \"mykey\", []byte(\"myvalue\"))\n}\n{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-api-guide.md#using-key-value-store-from-applications"},{"project":"spin","title":"Key Value Store","subheading":"Custom Key Value Stores","content":"Spin defines a key-value store named \"default\" and provides automatic backing storage. If you need to customize Spin with additional stores, or to change the backing storage for the default store, you can do so via the --runtime-config-file flag and the runtime-config.toml file. See undefined for details.","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-api-guide.md#custom-key-value-stores"},{"project":"spin","title":"Key Value Store","subheading":"Granting Key Value Store Permissions to Components","content":"By default, a given component of an app will not have access to any key value store. Access must be granted specifically to each component via the component manifest:[component]\n# Pass in 1 or more key value stores, based on how many you'd like your component to have access to\nkey_value_stores = [\"\", \"\"]For example, a component could be given access to the default store using key_value_stores = [\"default\"]","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-api-guide.md#granting-key-value-store-permissions-to-components"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/spin/kv-store-tutorial"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Key Value Store With Spin Applications","content":"Spin applications are best suited for event-driven, stateless workloads that have low-latency requirements. Keeping track of the application's state (storing information) is an integral part of any useful product or service. For example, users (and the business) will expect to store and load data/information at all times during an application’s execution. undefined has support for applications that need data in the form of key/value pairs and are satisfied by a Basically Available, Soft State, and Eventually Consistent (BASE) model. Workload examples include general value caching, session caching, counters, and serialized application state. In this tutorial, you will learn how to do the following:undefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#key-value-store-with-spin-applications"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Tutorial Prerequisites","content":"First, follow undefined to install Spin. To ensure you have the correct version, you can check with this command:$ spin --versionundefined","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#tutorial-prerequisites"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Creating a New Spin Application","content":"Let's create a Spin application that will send and retreive data from a key value store. To make things easy, we'll start from a template using the following commands (undefined):{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}$ spin new http-rust spin-key-value\n\n# Reference: https://github.com/fermyon/spin/tree/main/examples/rust-key-value{{ blockEnd }}{{ startTab \"TypeScript\" }}$ spin new http-ts spin-key-value\n\n# Reference: https://github.com/karthik2804/spin-kv-ts{{ blockEnd }}{{ startTab \"TinyGo\" }}$ spin new http-go spin-key-value\n\n# Reference: https://github.com/fermyon/spin/tree/main/examples/tinygo-key-value{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#creating-a-new-spin-application"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Configuration","content":"Good news - Spin will take care of setting up your key value store. However, in order to make sure your Spin application has permission to access the key value store, you must add the key_value_stores = [\"default\"] line in the [[component]] area of the spin.toml file. This line is necessary to communicate to Spin that a given component has access to the default key value store. A newly scaffolded Spin application will not have this line; you will need to add it.undefinedEach Spin application's key_value_stores instances are implemented on a per-component basis across the entire Spin application. This means that within a multi-component Spin application (which has the same key_value_stores = [\"default\"] configuration line), each [[component]] will access that same data store. If one of your application's components creates a new key/value pair, another one of your application's components can update/overwrite that initial key/value after the fact.","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#configuration"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"The Spin TOML File","content":"We will give our components access to the key value store by adding the key_value_stores = [\"default\"] in the [[component manifest]] as shown below:{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}spin_manifest_version = \"1\"\nauthors = [\"Fermyon Engineering \"]\ndescription = \"A simple application that exercises key-value storage.\"\nname = \"spin-key-value\"\ntrigger = { type = \"http\", base = \"/test\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"spin-key-value\"\nsource = \"target/wasm32-wasi/release/spin-key-value.wasm\"\nallowed_http_hosts = []\n# Gives this component access to the default key value store\nkey_value_stores = [\"default\"]\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"cargo build --target wasm32-wasi --release\"{{ blockEnd }}{{ startTab \"TypeScript\" }}spin_manifest_version = \"1\"\nauthors = [\"Fermyon Engineering \"]\ndescription = \"A simple application that exercises key-value storage.\"\nname = \"spin-key-value\"\ntrigger = { type = \"http\", base = \"/test\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"spin-key-value\"\nsource = \"target/spin-key-value.wasm\"\nexclude_files = [\"**/node_modules\"]\n# Gives this component access to the default key value store\nkey_value_stores = [\"default\"]\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"npm run build\"{{ blockEnd }}{{ startTab \"TinyGo\" }}spin_manifest_version = \"1\"\nauthors = [\"Fermyon Engineering \"]\ndescription = \"A simple application that exercises key-value storage.\"\nname = \"spin-key-value\"\ntrigger = { type = \"http\", base = \"/test\" }\nversion = \"1.0.0\"\n\n[[component]]\nid = \"spin-key-value\"\nsource = \"main.wasm\"\n# Gives this component access to the default key value store\nkey_value_stores = [\"default\"]\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go\"{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#the-spin-toml-file"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Write Code to Save and Load Data","content":"In this section, we use the Spin SDK to open and persist our application's data inside our default key/value store. This is a special store that every environment running Spin applications will make available for their application.","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#write-code-to-save-and-load-data"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"The Spin SDK Version","content":"If you have an existing application and would like to try out the key/value feature, please check the Spin SDK reference in your existing application's Cargo.toml file. If it refers to version 0.8 or earlier then update the Spin SDK reference to match your upgraded version of Spin. For example:# The Spin SDK.\nspin-sdk = { git = \"https://github.com/fermyon/spin\", tag = \"v0.10.0\" }Similarly an application created using the http-go template might need the reference to the Spin SDK in its go.mod file updated to look like the following:module github.com/http_go\n\ngo 1.17\n\nrequire github.com/fermyon/spin/sdk/go v0.10.0The same applies to other programming languages and their respective configuration. This information is provided to prevent you from experiencing an error such as the following:unresolved import spin_sdk::key_value\nkey_value::{Error, Store}\n^^^^^^^^^ could not find `key_value` in `spin_sdk`","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#the-spin-sdk-version"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Quick Overview - Video","content":"Before we get into the source code, let's watch a quick overview of the new key/value store feature.","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#quick-overview---video"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Source Code","content":"Now let's use the Spin SDK to:undefinedundefinedundefinedundefinedundefined{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}use anyhow::Result;\nuse http::{Method, StatusCode};\nuse spin_sdk::{\n http::{Request, Response},\n http_component,\n key_value::{Error, Store},\n};\n\n#[http_component]\nfn handle_request(req: Request) -> Result {\n // Open the default key-value store\n let store = Store::open_default()?;\n\n let (status, body) = match req.method() {\n &Method::POST => {\n // Add the request (URI, body) tuple to the store\n store.set(req.uri().path(), req.body().as_deref().unwrap_or(&[]))?;\n (StatusCode::OK, None)\n }\n &Method::GET => {\n // Get the value associated with the request URI, or return a 404 if it's not present\n match store.get(req.uri().path()) {\n Ok(value) => (StatusCode::OK, Some(value.into())),\n Err(Error::NoSuchKey) => (StatusCode::NOT_FOUND, None),\n Err(error) => return Err(error.into()),\n }\n }\n &Method::DELETE => {\n // Delete the value associated with the request URI, if present\n store.delete(req.uri().path())?;\n (StatusCode::OK, None)\n }\n &Method::HEAD => {\n // Like GET, except do not return the value\n match store.exists(req.uri().path()) {\n Ok(true) => (StatusCode::OK, None),\n Ok(false) => (StatusCode::NOT_FOUND, None),\n Err(error) => return Err(error.into()),\n }\n }\n // No other methods are currently supported\n _ => (StatusCode::METHOD_NOT_ALLOWED, None),\n };\n\n Ok(http::Response::builder().status(status).body(body)?)\n}{{ blockEnd }}{{ startTab \"TypeScript\"}}import { HandleRequest, HttpRequest, HttpResponse } from \"@fermyon/spin-sdk\"\n\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder()\n\nexport const handleRequest: HandleRequest = async function (request: HttpRequest): Promise {\n\n let store = spinSdk.kv.openDefault()\n let status = 200\n let body\n\n switch (request.method) {\n case \"POST\":\n store.set(request.uri, request.body || (new Uint8Array()).buffer)\n break;\n case \"GET\":\n let val\n try {\n val = store.get(request.uri)\n body = decoder.decode(val)\n } catch (error) {\n status = 404\n }\n break;\n case \"DELETE\":\n store.delete(request.uri)\n break;\n case \"HEAD\":\n if (!store.exists(request.uri)) {\n status = 404\n }\n break;\n default:\n }\n\n return {\n status: status,\n body: body\n }\n}{{ blockEnd }}{{ startTab \"TinyGo\" }}package main\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\n\tspin_http \"github.com/fermyon/spin/sdk/go/http\"\n\t\"github.com/fermyon/spin/sdk/go/key_value\"\n)\n\nfunc init() {\n\t// handler for the http trigger\n\tspin_http.Handle(func(w http.ResponseWriter, r *http.Request) {\n\t\tstore, err := key_value.Open(\"default\")\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tdefer key_value.Close(store)\n\n\t\tbody, err := io.ReadAll(r.Body)\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tswitch r.Method {\n\t\tcase http.MethodPost:\n\t\t\terr := key_value.Set(store, r.URL.Path, body)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tw.WriteHeader(http.StatusOK)\n\t\tcase http.MethodGet:\n\t\t\tvalue, err := key_value.Get(store, r.URL.Path)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tw.WriteHeader(http.StatusOK)\n\t\t\tw.Write(value)\n\t\tcase http.MethodDelete:\n\t\t\terr := key_value.Delete(store, r.URL.Path)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tw.WriteHeader(http.StatusOK)\n\t\tcase http.MethodHead:\n\t\t\texists, err := key_value.Exists(store, r.URL.Path)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif exists {\n\t\t\t\tw.WriteHeader(http.StatusOK)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tw.WriteHeader(http.StatusNotFound)\n\t\tdefault:\n\t\t\thttp.Error(w, \"method not allowed\", http.StatusMethodNotAllowed)\n\t\t}\n\t})\n}\n\nfunc main() {}{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#source-code"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Building and Deploying Your Spin Application","content":"Now let's build and deploy our Spin Application locally. Run the following command to build your application:$ spin build","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#building-and-deploying-your-spin-application"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Storing and Retrieving Data From Your Default Key/Value Store","content":"Once you have completed this minimal configuration and deployed your application, data will be persisted across requests. Let's begin by creating a POST request that stores a JSON key/value object:# Create a new POST request and set the key/value pair of foo:bar\n$ curl -X POST localhost:3000/test -H 'Content-Type: application/json' -d '{\"foo\":\"bar\"}' -v\n\nTrying 127.0.0.1:3000...\nConnected to localhost (127.0.0.1) port 3000\nPOST /test HTTP/1.1\nHost: localhost:3000\nContent-Type: application/json\nHTTP/1.1 200 OKWe can now use a HEAD request to confirm that our component is holding data for us. Essentially, all we want to see here is a 200 OK response when calling our components endpoint (/test). Let's give it a try:curl -I HEAD localhost:3000/test -v \n\nTrying 127.0.0.1:3000...\n* Connected to localhost (127.0.0.1) port 3000\nHEAD /test HTTP/1.1\nHost: localhost:3000\nHTTP/1.1 200 OKPerfect, 200 OK. Now, let's create a GET request that fetches the data from our component:# Create a GET request and fetch the key/value that we stored in the previous request\n$ curl -X GET localhost:3000/test -v\n\nTrying 127.0.0.1:3000...\nConnected to localhost (127.0.0.1) port 3000\nGET /test HTTP/1.1\nHost: localhost:3000\nHTTP/1.1 200 OK\n{\n \"foo\": \"bar\"\n}Great!, the above command successfully returned our data as intended:{\n \"foo\": \"bar\"\n}Lastly, we show how to create a DELETE request that removes the data for this specific component altogether:$ curl -X DELETE localhost:3000/test -v\n\nTrying 127.0.0.1:3000...\nConnected to localhost (127.0.0.1) port 3000\nDELETE /test HTTP/1.1\nHost: localhost:3000\nHTTP/1.1 200 OKNote how all of the above commands returned 200 OK responses. In these examples, we were able to POST, HEAD (check to see if data exists), GET and also DELETE data from our component.Interestingly there is one more request we can re-run before wrapping up this tutorial. If no data exists in the component's endpoint of /test (which is technically the case now that we have sent the DELETE request) the HEAD request should correctly return 404 Not Found. You can consider this a type of litmus test; let's try it out:$ curl -I HEAD localhost:3000/test -v\n\nTrying 127.0.0.1:3000...\nConnected to localhost (127.0.0.1) port 3000\nHEAD /test HTTP/1.1\nHost: localhost:3000\nHTTP/1.1 404 Not FoundAs we can see above, there is currently no data found at the /test endpoint of our application.","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#storing-and-retrieving-data-from-your-default-keyvalue-store"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Conclusion","content":"We want to get feedback on the ergonomics of the key value API. We are curious about what new APIs you would suggest we implement and are also interested in learning about what backing stores you would like to see.","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#conclusion"},{"project":"spin","title":"Persistent Data Locally With Key Value Store","subheading":"Next Steps","content":"You can read the undefined as well as the implementation for the undefined. Please feel free to ask questions and also share your thoughts in undefined.","keywords":"","subsectionKeywords":"","url":"/spin/kv-store-tutorial.md#next-steps"},{"project":"spin","title":"Language Support Overview","subheading":"","content":"This page contains information about language support for Spin features:{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}| Feature | SDK Supported? |\n|-----|-----|\n| undefined |\n| undefined | Supported |\n| undefined | Supported |\n| undefined |\n| undefined | Supported |\n| undefined | Supported |\n| undefined | Supported |\n| undefined | Supported |\n| undefined | Supported |\n| undefined |\n| undefined | Supported |{{ blockEnd }}{{ startTab \"TypeScript\"}}| Feature | SDK Supported? |\n|-----|-----|\n| undefined |\n| undefined | Supported |\n| Redis | Not Supported |\n| undefined |\n| undefined | Supported |\n| undefined | Supported |\n| MySQL | Not Supported |\n| PostgreSQL| Not Supported |\n| undefined | Supported |\n| undefined |\n| Authoring Custom Triggers | Not Supported |{{ blockEnd }}{{ startTab \"Python\"}}| Feature | SDK Supported? |\n|-----|-----|\n| undefined |\n| undefined | Supported |\n| Redis | Not Supported |\n| undefined |\n| undefined | Supported |\n| undefined | Supported |\n| MySQL | Not Supported |\n| PostgreSQL | Not Supported |\n| undefined | Supported |\n| undefined |\n| Authoring Custom Triggers | Not Supported |{{ blockEnd }}{{ startTab \"TinyGo\"}}| Feature | SDK Supported? |\n|-----|-----|\n| undefined |\n| undefined | Supported |\n| undefined | Supported |\n| undefined |\n| undefined | Supported |\n| undefined | Supported |\n| MySQL | Not Supported |\n| PostgreSQL | Not Supported |\n| undefined | Supported |\n| undefined |\n| Authoring Custom Triggers | Not Supported |{{ blockEnd }}{{ startTab \"C#\"}}| Feature | SDK Supported? |\n|-----|-----|\n| undefined |\n| undefined | Supported |\n| Redis | Not Supported |\n| undefined |\n| undefined | Supported |\n| Key Value Storage | Not Supported |\n| MySQL | Not Supported |\n| undefined | Supported |\n| undefined | Supported |\n| undefined |\n| Authoring Custom Triggers | Not Supported |{{ blockEnd }}{{ blockEnd }","subsectionKeywords":"","url":"/spin/language-support-overview"},{"project":"spin","title":"Managing Plugins","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedPlugins are a way to extend the functionality of Spin. Spin provides commands for installing and removing them, so you don't need to use separate installation tools. When you have installed a plugin into Spin, you can call it as if it were a Spin subcommand. For example, the JavaScript SDK uses a tool called js2wasm to package JavaScript code into a Wasm module, and JavaScript applications run it via the spin js2wasm command.","subsectionKeywords":"","url":"/spin/managing-plugins"},{"project":"spin","title":"Managing Plugins","subheading":"Installing Plugins","content":"To install plugins, use the spin plugins install command. You can install plugins by name from a curated repository, or other plugins from a URL or file system.","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#installing-plugins"},{"project":"spin","title":"Managing Plugins","subheading":"Installing Well Known Plugins","content":"The Spin maintainers curate a catalogue of \"known\" plugins. You can install plugins from this catalogue by name:$ spin plugins install js2wasmSpin checks that the plugin is available for your version of Spin and your operating system, and prompts you to confirm the installation. To skip the prompt, pass the --yes flag.undefined","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#installing-well-known-plugins"},{"project":"spin","title":"Managing Plugins","subheading":"Installing a Specific Version of a Plugin","content":"To install a specific version of a plugin, pass the --version flag:$ spin plugins install js2wasm --version 0.4.0","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#installing-a-specific-version-of-a-plugin"},{"project":"spin","title":"Managing Plugins","subheading":"Installing a Plugin From a URL","content":"If the plugin you want has been published on the Web but has not been added to the catalogue, you can install it from its manifest URL. The manifest is the JSON document that links to the binaries for different operating systems and processors. For example:$ spin plugins install --url https://github.com/fermyon/spin-befunge-sdk/releases/download/v1.4.0/befunge2wasm.json","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#installing-a-plugin-from-a-url"},{"project":"spin","title":"Managing Plugins","subheading":"Installing a Plugin From a File","content":"If the plugin you want is in your local file system, you can install it from its manifest file path. The manifest is the JSON document that links to the binaries for different operating systems and processors. For example:$ spin plugins install --file ~/dev/spin-befunge-sdk/befunge2wasm.json","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#installing-a-plugin-from-a-file"},{"project":"spin","title":"Managing Plugins","subheading":"Running a Plugin","content":"You run plugins in the same way as built-in Spin subcommands. For example:$ spin js2wasm --help","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#running-a-plugin"},{"project":"spin","title":"Managing Plugins","subheading":"Viewing Available Plugins","content":"To see what plugins are available in the catalogue, run spin plugins list:$ spin plugins list\nbefunge2wasm 1.4.0 [incompatible]\njs2wasm 0.3.0 [installed]\njs2wasm 0.4.0\npy2wasm 0.1.0 [installed]\ntrigger-sqs 0.1.0undefinedThe annotations by the plugins show their status and compatibility:| Annotation | Meaning |\n|---------------------------------|---------|\n| [incompatible] | The plugin does not run on your operating system or processor. |\n| [installed] | You have the plugin already installed and available to run. |\n| [requires other Spin version] | The plugin can run on your operating system and processor, but is not compatible with the version of Spin you are running. The annotation indicates which versions of Spin it is compatible with. |","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#viewing-available-plugins"},{"project":"spin","title":"Managing Plugins","subheading":"Viewing Installed Plugins","content":"To see only the plugins you have installed, run spin plugins list --installed.","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#viewing-installed-plugins"},{"project":"spin","title":"Managing Plugins","subheading":"Uninstalling Plugins","content":"You can uninstall plugins using spin plugins uninstall with the plugin name:$ spin plugins uninstall befunge2wasm","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#uninstalling-plugins"},{"project":"spin","title":"Managing Plugins","subheading":"Upgrading Plugins","content":"To upgrade a plugin to the latest version, run spin plugins upgrade. This has the same options as spin plugins install, according to whether the plugin comes from the catalogue, a URL, or a file:$ spin plugins upgrade js2wasm\n$ spin plugins upgrade --url https://github.com/fermyon/spin-befunge-sdk/releases/download/v1.7.0/befunge2wasm.json\n$ spin plugins upgrade --file ~/dev/spin-befunge-sdk/befunge2wasm.jsonundefinedBy default, Spin will only undefined plugins. If you want to allow Spin to roll back to an earlier version, pass the --downgrade flag.","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#upgrading-plugins"},{"project":"spin","title":"Managing Plugins","subheading":"Refreshing the Catalogue","content":"The first time you install a plugin from the catalogue, Spin creates a local cache of the catalogue. It continues to use this local cache for future install, list and upgrade commands; this is similar to OS package managers such as apt, and avoids rate limiting on the catalogue. However, this means that in order to see new catalogue entries - new plugins or new versions - you must first update the cache.To update your local cache of the catalogue, run spin plugins update.","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#refreshing-the-catalogue"},{"project":"spin","title":"Managing Plugins","subheading":"Next Steps","content":"undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/managing-plugins.md#next-steps"},{"project":"spin","title":"Managing Templates","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedTemplates are a Spin tool for scaffolding new applications and components. You can use them via the spin new and spin add commands. For more information about creating applications with templates, see undefined.","subsectionKeywords":"","url":"/spin/managing-templates"},{"project":"spin","title":"Managing Templates","subheading":"Installing Templates","content":"undefinedTo install templates, use the spin templates install command. You can install templates from a Git repository, or while undefined you can install them from a local directory.","keywords":"","subsectionKeywords":"","url":"/spin/managing-templates.md#installing-templates"},{"project":"spin","title":"Managing Templates","subheading":"Installing From the Spin Git Repository","content":"To install templates from the Spin Git repository, run spin templates install --git.$ spin templates install --git https://github.com/fermyon/spinThe above command installs undefined templates in the repository.undefined","keywords":"","subsectionKeywords":"","url":"/spin/managing-templates.md#installing-from-the-spin-git-repository"},{"project":"spin","title":"Managing Templates","subheading":"Installing From a Specific Branch","content":"By default, if you install templates from a Git repository, Spin tries to find a repo tag that matches the version of Spin, and installs from that tag. Failing this, it installs from HEAD. If you would like to install from a specific tag or branch, pass the --branch option:$ spin templates install --git https://github.com/fermyon/spin --branch spin/templates/v0.8","keywords":"","subsectionKeywords":"","url":"/spin/managing-templates.md#installing-from-a-specific-branch"},{"project":"spin","title":"Managing Templates","subheading":"Installing From a Local Directory","content":"To install templates from your local file system, run spin templates install --dir.undefined# Expects to find a directory ~/dev/spin-befunge-sdk/templates\n$ spin templates install --dir ~/dev/spin-befunge-sdkSee undefined for more details on this layout.","keywords":"","subsectionKeywords":"","url":"/spin/managing-templates.md#installing-from-a-local-directory"},{"project":"spin","title":"Managing Templates","subheading":"Viewing Your Installed Templates","content":"To see what templates you have installed, run spin templates list.You can use the --verbose option to see additional information such as where they were installed from.","keywords":"","subsectionKeywords":"","url":"/spin/managing-templates.md#viewing-your-installed-templates"},{"project":"spin","title":"Managing Templates","subheading":"Uninstalling Templates","content":"You can uninstall templates using spin templates uninstall with the template name:$ spin templates uninstall redis-befungeundefined","keywords":"","subsectionKeywords":"","url":"/spin/managing-templates.md#uninstalling-templates"},{"project":"spin","title":"Managing Templates","subheading":"Upgrading Templates","content":"When you upgrade Spin, you will typically want to upgrade your templates to match. This means new applications and components will get dependencies that match the Spin version you are using. To do this, run spin templates upgrade:$ spin templates upgrade\nSelect repos to upgrade. Use Space to select/deselect and Enter to confirm selection.\n [x] https://github.com/fermyon/spin-python-sdk\n [ ] https://github.com/fermyon/spin (at spin/templates/v1.0)\n> [x] https://github.com/fermyon/spin-js-sdkUse the cursor keys and the space bar to select the repositories you want to upgrade, then hit Enter to upgrade the selected repositories.undefinedIf you want to upgrade undefined repositories without being prompted, run spin templates upgrade --all.As mentioned above, if you want to check which templates come from which repositories use --verbose i.e. spin templates list --verbose.","keywords":"","subsectionKeywords":"","url":"/spin/managing-templates.md#upgrading-templates"},{"project":"spin","title":"Managing Templates","subheading":"Upgrading Templates From a Local Directory","content":"spin templates upgrade only upgrades from Git repositories. If you want to upgrade and your templates are in a local directory, run the spin templates install command with the --upgrade flag:$ spin templates install --dir ~/dev/spin-befunge-sdk --upgrade","keywords":"","subsectionKeywords":"","url":"/spin/managing-templates.md#upgrading-templates-from-a-local-directory"},{"project":"spin","title":"Managing Templates","subheading":"Next Steps","content":"undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/managing-templates.md#next-steps"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedThis page describes the contents of the Spin manifest file, typically called spin.toml.","subsectionKeywords":"","url":"/spin/manifest-reference"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"Format","content":"The manifest is a TOML file, and follows standard TOML syntax. See the undefined for information about the TOML syntax.","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#format"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"Manifest Fields","content":"| Name | Required? | Type | Value | Example |\n|-------------------------|------------|-------------|----------|-----------|\n| spin_manifest_version | Required | String | The version of the file format that the rest of the manifest follows. Currently, this value must be \"1\". | \"1\" |\n| name | Required | String | The name of the application. This may be any string of alphanumeric characters, hyphens and underscores. | \"hello-world\" |\n| version | Required | String | The version of the application. The must be a string of the form major.minor.patch, where each element is a number. | \"1.0.5\" |\n| description | Optional | String | A human-readable description of the application. | \"The best app for all your world-greeting needs\" |\n| authors | Optional | Array of strings | The authors of the applications. If present, this must ba an array, even if it has only one entry. | [\"Jane Q Hacker ()\"] |\n| trigger | Required | Table | The trigger for the application - that is, the kind of event that the application responds to. The table must contain the type field, and may contain others depending on the value of type. See undefined for details. | { type = \"http\", base = \"/\" } |\n| variables | Optional | Table | Dynamic configuration variables which the user can set when they run the application. See undefined below. | [variables]
message = { default = \"hello\" } |\n| component | Required | Table array | A manifest must contain at least one component table. component is always an array, even if there is only one component, so always use double square brackets. See undefined below. | [[component]]
id = \"hello\" |","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#manifest-fields"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"The trigger Table","content":"The trigger table specifies the events that the application responds to. The type field is always required, but the other fields depend on the type. This section describes the built-in http and redis trigger types.undefinedtrigger = { type = \"http\", base = \"/\" }","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#the-trigger-table"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"The trigger Table for HTTP Applications","content":"| Name | Required? | Type | Value | Example |\n|-------------------------|------------|-------------|----------|-----------|\n| type | Required | String | Always \"http\" for HTTP applications. | \"http\" |\n| base | Required | String | The base path of the application. All component routes are relative to this. It allows multiple applications to be mounted under the same host. | \"/\" |","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#the-trigger-table-for-http-applications"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"The trigger Table for Redis Applications","content":"| Name | Required? | Type | Value | Example |\n|-------------------------|------------|-------------|----------|-----------|\n| type | Required | String | Always \"redis\" for Redis applications. | \"redis\" |\n| address | Required | String | The address of the Redis instance the components are using the message subscriptions. Use the redis: URL scheme. | \"redis://localhost:6379\" |","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#the-trigger-table-for-redis-applications"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"The variables Table","content":"The keys of variables table are user-defined. The value of each key is another table with the fields shown in the following table.undefined[variables]\nvessel = { default = \"teapot\" }\ntoken = { required = true, secret = true }| Name | Required? | Type | Value | Example |\n|-------------------------|------------|-------------|----------|-----------|\n| default | Optional | String | The value of the variable if no value is supplied at runtime. If specified, the value must be a string. If not specified, required must be true. | \"teapot\" |\n| required | Optional | Boolean | Whether a value must be supplied at runtime. If not specified, required defaults to false, and default must be provided | false |\n| secret | Optional | Boolean | If set, this variable should be treated as sensitive. | false |","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#the-variables-table"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"The component Tables","content":"component is a table array, meaning each component is introduced with double-bracket syntax. Subtables are written using single-bracket syntax or inline JSON syntax. For example:[[component]]\nid = \"hello\"\nsource = \"hello.wasm\"\n[component.trigger]\nroute = \"/hello\"Each table in the component array contains the following fields:| Name | Required? | Type | Value | Example |\n|-------------------------|------------|-------------|----------|-----------|\n| id | Required | String | An identifier for this component, unique within the application. This may be any string of alphanumeric characters, hyphens and underscores. | \"cart-api\" |\n| description | Optional | String | A human-readable description of the component. | \"The shopping cart API\" |\n| source | Required | String or table | The Wasm module which should handle the component. This must be built to work with the application trigger. It can be in one of the following formats: | |\n| | | String | * The path to a Wasm file (relative to the manifest file) | dist/cart.wasm |\n| | | Table | * The URL of a Wasm file downloadable over HTTP. This must be a table containing a url field for the Wasm file, and a digest field contains a SHA256 hex digest, used to check integrity. | { url = \"https://example.com/example.wasm\", digest = \"sha256:6503...2375\" } |\n| files | Optional | Array of strings and/or tables | The files to be made available to the Wasm module at runtime. This is an array, and each element of the array is either: | [ \"images/*.jpg\", { source = \"assets/images\", destination = \"/pictures\" } ] |\n| | | String | * A file path or glob pattern, relative to the manifest file. The matching file or files will be available in the Wasm module at the same relative paths. | \"images/*.jpg\" |\n| | | Table | * A directory to be made available to the Wasm module at a specific path. This must be a table containing a source field for the directory relative to the manifest file, and a destination field containing the absolute path at which to make it available. | { source = \"assets/images\", destination = \"/pictures\" } |\n| exclude_files | Optional | Array of strings | Any files or glob patterns that should undefined be available to the Wasm module at runtime, even though they match a files entry. | [assets/images/test/**/*.*] |\n| allowed_http_hosts | Optional | Array of strings | The host names or addresses to which the Wasm module is allowed to send HTTP requests. If the name includes a port, the Wasm module can send requests only to that port; otherwise, the Wasm module can send requests only to the default port for the scheme it uses. The special string insecure:allow-all permits the module to send HTTP requests to undefined host, but is intended for development use only; some deployment environments may decline to honour it. | [\"example.com\", \"localhost:8081\"] |\n| key_value_stores | Optional | Array of strings | An array of key-value stores that the Wasm module is allowed to read or write. A store named default is provided by the Spin runtime, though modules must still be permitted to access it. In current versions of Spin, \"default\" is the only store allowed. | [\"default\"] |\n| environment | Optional | Table | Environment variables to be set for the Wasm module. This is a table. The table keys are user-defined; the values must be strings. | { DB_URL = \"mysql://spin:spin@localhost/dev\" } |\n| trigger | Required | Table | Specifies how this component is triggered. This is a table, whose contents of are trigger-specific; see below. | [component.trigger]
route = \"/...\" |\n| build | Optional | Table | The command that spin build uses to build this component. See undefined below. | [component.build]
command = \"npm run build\" |\n| config | Optional | Table | Dynamic configuration values to be made available to this component. The table keys are user-defined; the values must be strings, and may use template notation as described under undefined. | [component.config]
api_base_url = \"https://{{ api_host }}/v1\" |","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#the-component-tables"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"The component.trigger Table for HTTP Applications","content":"| Name | Required? | Type | Value | Example |\n|-------------------------|------------|-------------|----------|-----------|\n| route | Required | String | The route which this component handles. Requests to the route will cause the component to execute. This may be an exact route (/example), which matches on the given path, or a wildcard route indicated by the suffix /... (/example/...), which matches any route under this prefix. If two routes overlap, requests are directed to the matching route with the longest prefix - see undefined for details and examples. | \"/api/cart/...\" |\n| executor | Optional | Table | How Spin should invoke the component. If present, this is a table. The type key is required and may have the values \"spin\" or \"wagi\". If omitted. the default is { type = \"spin\"}. See undefined for details. | { type = \"wagi\" } |\n| | | | If type = \"spin\" there are no other keys defined. In this case, Spin calls the component using a standard Wasm component interface. Components built using Spin SDKs or Spin interface files use this convention. | { type = \"spin\" } |\n| | | | If type = \"wagi\", Spin calls the component's \"main\" (_start) function using undefined. Components built using languages or toolchains that do not support Wasm interfaces will need to be called in this way. In this case, the following additional keys may be set:

* argv (optional): The string representation of the argv list that should be passed into the handler. ${SCRIPT_NAME} will be replaced with the script name, and ${ARGS} will be replaced with the query parameters of the request, formatted as arguments. The default is to follow the CGI specification, and pass ${SCRIPT_NAME} ${ARGS}

* entrypoint (optional): The name of the function to call as the entry point to this handler. By default, it is _start (which in most languages translates to main in the source code).

See undefined for details. | { type = \"wagi\" } |","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#the-componenttrigger-table-for-http-applications"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"The component.trigger Table for Redis Applications","content":"| Name | Required? | Type | Value | Example |\n|-------------------------|------------|-------------|----------|-----------|\n| channel | Required | String | The Redis channel which this component handles. Messages on this channel will cause the component to execute. | \"purchases\" |","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#the-componenttrigger-table-for-redis-applications"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"The component.build Table","content":"| Name | Required? | Type | Value | Example |\n|-------------------------|------------|-------------|----------|-----------|\n| command | Required | String | The command to execute on spin build. | \"cargo build --target wasm32-wasi --release\" |\n| workdir | Optional | String | The directory in which to execute command, relative to the manifest file. The default is the directory containing the manifest file. An example of where this is needed is a multi-component application where each component is its own source tree in its own directory. | \"my-project\" |","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#the-componentbuild-table"},{"project":"spin","title":"Spin Application Manifest Reference","subheading":"Next Steps","content":"undefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/manifest-reference.md#next-steps"},{"project":"spin","title":"Building Spin components in other languages","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedWebAssembly is becoming undefined, and as language toolchains add support for the\nundefined,\nbuilding Spin components will also become supported.As a general rule:undefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/spin/other-languages"},{"project":"spin","title":"Building Spin components in other languages","subheading":"AssemblyScript","content":"undefined is a TypeScript-based language that compiles directly to WebAssembly.\nAssemblyScript has WASI/Wagi support, and so can be used with Spin.undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/other-languages.md#assemblyscript"},{"project":"spin","title":"Building Spin components in other languages","subheading":"C/C++","content":"C and C++ are both broadly supported in the WebAssembly ecosystem. WASI/Wagi support means that both can be used to write Spin apps.undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/other-languages.md#cc"},{"project":"spin","title":"Building Spin components in other languages","subheading":"C# and .NET Languages","content":".NET has experimental support for WASI, so many (if not all) .NET languages, including C# and F#, can be used to write Spin applications.undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/other-languages.md#c-and-net-languages"},{"project":"spin","title":"Building Spin components in other languages","subheading":"Grain","content":"undefined, a new functional programming language, has WASI/Wagi support and can be used to write Spin apps.undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/other-languages.md#grain"},{"project":"spin","title":"Building Spin components in other languages","subheading":"Python","content":"Python's interpreter can be compiled to WebAssembly, and it has WASI support. It is known to work for Spin.undefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/other-languages.md#python"},{"project":"spin","title":"Building Spin components in other languages","subheading":"Ruby","content":"Upstream undefined officially supports WebAssembly and WASI, and we here at Fermyon have successfully run Ruby apps in Spin.undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/other-languages.md#ruby"},{"project":"spin","title":"Building Spin components in other languages","subheading":"Zig","content":"Zig is a low-level systems language that has support for Wasm and WASI, and can be used to write Spin apps.undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/other-languages.md#zig"},{"project":"spin","title":"Creating Spin Plugins","subheading":"","content":"undefinedundefinedundefinedSpin plugins add new functionality or subcommands to Spin without modifying the\nSpin codebase. They make Spin easily extensible while keeping it lightweight.\nSpin plugins can add new triggers to Spin (such as the undefined),\nenable new language SDKs (such as\nundefined),\nand more.This document will cover what Spin plugins are, how to use a plugin, and how to\ncreate a plugin.","subsectionKeywords":"","url":"/spin/plugin-authoring"},{"project":"spin","title":"Creating Spin Plugins","subheading":"What Are Spin Plugins?","content":"A Spin plugin is an executable that is added to Spin's plugins directory\n($XDG_DATA_HOME/spin/plugins) upon a spin plugins install . The\nplugin is then ready to be used. If the plugin is an extension to the Spin CLI,\nit can now be executed directly as a subcommand: spin . If the\nplugin is a trigger plugin, it will be executed during spin up when an app\nusing that trigger is run.While for now plugins are assumed to be executables, in the future, support for\nplugging in WebAssembly modules may be desirable.","keywords":"","subsectionKeywords":"","url":"/spin/plugin-authoring.md#what-are-spin-plugins"},{"project":"spin","title":"Creating Spin Plugins","subheading":"How to Find and Use a Spin Plugin","content":"Spin maintains a centralized catalogue of available Spin plugins in the undefined. During plugin\ninstallation, if it does not already exist, Spin fetches the remote catalogue\nand creates a local snapshot. To ensure that the local snapshot is up to date,\nit is best to run spin plugins update before installing any plugins.To list available plugins, run spin plugins list. Now, decide which plugin to\ninstall. For example, the js2wasm plugin, which is needed in order to build\nJavaScript Spin applications, can be installed by running:$ spin plugins install js2wasmWith the plugin installed, you can now call spin js2wasm to run it. In this\ncase, for example, you might call it from your JavaScript application's npm\nbuild script. Learn more about building Spin components in JavaScript\nundefined.To upgrade installed plugins to newer versions, run spin plugin update to\nfetch the latest plugins to the local catalogue and spin plugin upgrade to perform the\nupgrade on the installed plugins.","keywords":"","subsectionKeywords":"","url":"/spin/plugin-authoring.md#how-to-find-and-use-a-spin-plugin"},{"project":"spin","title":"Creating Spin Plugins","subheading":"Authoring a Spin Plugin","content":"Spin plugins are implemented as a manifest that points to one or more .tar.gz archives which contain the plugin executables. So, to create a plugin you must:undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/plugin-authoring.md#authoring-a-spin-plugin"},{"project":"spin","title":"Creating Spin Plugins","subheading":"Packaging a Plugin","content":"After creating your plugin executable, package it along with its license as a\ntar.gz archive. Note that the name field in the plugin manifest must match\nboth the binary and license name. See the undefined\nfor more details on naming conventions.Refer to the aptly named undefined for an\nexample of how to build a plugin.","keywords":"","subsectionKeywords":"","url":"/spin/plugin-authoring.md#packaging-a-plugin"},{"project":"spin","title":"Creating Spin Plugins","subheading":"Creating a Spin Plugin Manifest","content":"A Spin plugin manifest is a JSON file that conforms to the undefined.\nA manifest defines a plugin’s name, version, license, homepage (i.e. GitHub\nrepo), compatible Spin version, and gives a short description of the plugin. It\nalso lists the URLs of the tar archives of the plugin for various operating\nsystems and platforms. The URL can point to the local path to the file by using\nthe file scheme file://, for example, file:///tmp/my-plugin.tar.gz.To ensure your plugin manifest is valid, follow the steps in the undefined.","keywords":"","subsectionKeywords":"","url":"/spin/plugin-authoring.md#creating-a-spin-plugin-manifest"},{"project":"spin","title":"Creating Spin Plugins","subheading":"Installing a Local Plugin","content":"By default, Spin will look in the plugins catalogue for a plugin. However, when\ndeveloping and testing a plugin, it is unlikely to be in the the catalogue. For\nboth installs and upgrades, the --file or --url flags can be used to point\nto specific local or remote plugin manifests. For example, a local manifest\ncalled practice.json can be installed and run as follows:$ spin plugin install --file practice.json\n$ spin practice","keywords":"","subsectionKeywords":"","url":"/spin/plugin-authoring.md#installing-a-local-plugin"},{"project":"spin","title":"Creating Spin Plugins","subheading":"Contributing a Plugin","content":"If you think the community would benefit from your newly created plugin, create\na PR to add it to the undefined!","keywords":"","subsectionKeywords":"","url":"/spin/plugin-authoring.md#contributing-a-plugin"},{"project":"spin","title":"Building Spin Components in Python","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedWith Python being a very popular language, Spin provides support for building components with Python; undefined. The development of the Python SDK is continually being worked on to improve user experience and also add new features.undefinedundefinedundefined There is a blog article undefined if you are interested in some further reading; in addition to this technical documentation.","subsectionKeywords":"","url":"/spin/python-components"},{"project":"spin","title":"Building Spin Components in Python","subheading":"Spin's Python Plugin","content":"To compile Python programs to Spin components, you need to install a Spin plugin called py2wasm. The following commands will ensure that you have the latest version of the plugin installed:# Fetch all of the latest Spin plugins from the spin-plugins repository\n$ spin plugin update\n# Install py2wasm plugin\n$ spin plugin install py2wasmundefined For more information about managing spin plugins, see the undefined in the Spin Command Line Interface (CLI) documentation.","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#spins-python-plugin"},{"project":"spin","title":"Building Spin Components in Python","subheading":"Spin's Python HTTP Request Handler Template","content":"Spin's Python HTTP Request Handler Template can be installed from undefined using the following command:$ spin templates install --git https://github.com/fermyon/spin-python-sdk --updateThe above command will install the http-py template and produce an output similar to the following:Copying remote template source\nInstalling template http-py...\nInstalled 1 template(s)\n\n+---------------------------------------------+\n| Name Description |\n+=============================================+\n| http-py HTTP request handler using Python |\n+---------------------------------------------+undefined For more information about managing spin templates, see the undefined in the Spin Command Line Interface (CLI) documentation.","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#spins-python-http-request-handler-template"},{"project":"spin","title":"Building Spin Components in Python","subheading":"Structure of a Python Component","content":"A new Python component can be created using the following command:$ spin new http-py hello-world --accept-defaultsThis creates a directory of the following structure:hello-world/\n├── app.py\n├── Pipfile\n└── spin.tomlThe spin.toml file will look similar to the following:spin_manifest_version = \"1\"\nauthors = [\"Fermyon Engineering \"]\ndescription = \"\"\nname = \"hello-world\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-world\"\nsource = \"app.wasm\"\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"spin py2wasm app -o app.wasm\"","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#structure-of-a-python-component"},{"project":"spin","title":"Building Spin Components in Python","subheading":"A Simple HTTP Components Example","content":"In Spin, HTTP components are triggered by the occurrence of an HTTP request and must return an HTTP response at the end of their execution. Components can be built in any language that compiles to WASI. If you would like additional information about building HTTP applications you may find undefined useful.Building a Spin HTTP component using the Python SDK means writing a single function that takes an HTTP request as a parameter, and returns an HTTP response. Here is an example of the default Python code which the previous spin new created for us; a simple example of a request/response:from spin_http import Response\n\ndef handle_request(request):\n return Response(200,\n [(\"content-type\", \"text/plain\")],\n bytes(f\"Hello from the Python SDK\", \"utf-8\"))The important things to note in the implementation above:undefinedundefinedThe source code for this Python HTTP component example is in the app.py file. The app.py file is compiled into a .wasm module thanks to the py2wasm plugin. This all happens behind the scenes.","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#a-simple-http-components-example"},{"project":"spin","title":"Building Spin Components in Python","subheading":"Building and Running the Application","content":"All you need to do is run the spin build command from within the project's directory; as shown below:$ cd hello-world\n$ spin buildEssentially, we have just created a new Spin compatible module which can now be run using the spin up command, as shown below:$ spin upWith Spin running our application in our terminal, we can now go ahead (grab a new terminal) and call the Spin application via an HTTP request:$ curl -i localhost:3000/hello\n\nHTTP/1.1 200 OK\ncontent-type: text/plain\ncontent-length: 25\n\nHello from the Python SDKundefined","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#building-and-running-the-application"},{"project":"spin","title":"Building Spin Components in Python","subheading":"An Outbound HTTP Example","content":"This next example will create an outbound request, to obtain a random fact about dogs, which will be returned to the calling code. If you would like to try this out, you can go ahead and update your existing app.py file from the previous step; using the following source code:from spin_http import Request, Response, http_send\nfrom os import environ\n\n\ndef handle_request(request):\n\n response = http_send(\n Request(\"GET\", \"https://some-random-api.ml/facts/dog\", [], None))\n\n return Response(200,\n [(\"content-type\", \"text/plain\")],\n bytes(f\"Here is a dog fact: {str(response.body, 'utf-8')}, the environment: {environ}\", \"utf-8\"))","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#an-outbound-http-example"},{"project":"spin","title":"Building Spin Components in Python","subheading":"Configuration","content":"The Spin framework protects your code from making outbound requests to just any URL. For example, if we try to run the above code undefined, we will correctly get the following error AssertionError: HttpError::DestinationNotAllowed. To allow our component to request the some-random-api.ml domain, all we have to do is add that domain to the specific component of the application that is making the request. Here is an example of an updated spin.toml file where we have added allowed_http_hosts:spin_manifest_version = \"1\"\nauthors = [\"Fermyon Engineering \"]\ndescription = \"\"\nname = \"hello-world\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-world\"\nsource = \"app.wasm\"\nallowed_http_hosts = [\"https://some-random-api.ml\"]\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"spin py2wasm app -o app.wasm\"","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#configuration"},{"project":"spin","title":"Building Spin Components in Python","subheading":"Building and Running the Application","content":"If we re-build the application with this new configuration and re-run, we will get our new dog fact:$ spin build\n$ spin upA new request now correctly returns a dog fact from the some-random API endpoint.$ curl -i localhost:3000/hello\n\nHTTP/1.1 200 OK\ncontent-type: text/plain\ncontent-length: 130\n\nHere is a dog fact: {\"fact\":\"Dogs and wolves split from a common ancestor around 34,000 years ago.\"} ","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#building-and-running-the-application"},{"project":"spin","title":"Building Spin Components in Python","subheading":"An Outbound Redis Example","content":"In this final example, we talk to an existing Redis instance. You can find the official undefined. We also gave a quick run-through on setting up Redis with Spin in our previous article called undefined, so please take a look at that blog if you need a hand.","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#an-outbound-redis-example"},{"project":"spin","title":"Building Spin Components in Python","subheading":"Configuration","content":"After installing Redis on localhost, we simply add the config = { redis_address = \"redis://127.0.0.1:6379\" } to the spin.toml file, as shown below:spin_manifest_version = \"1\"\nauthors = [\"Fermyon Engineering \"]\ndescription = \"\"\nname = \"hello-world\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-world\"\nsource = \"app.wasm\"\nconfig = { redis_address = \"redis://127.0.0.1:6379\" }\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"spin py2wasm app -o app.wasm\"If you are still following along, please go ahead and update your app.py file one more time, as follows:from spin_http import Response\nfrom spin_redis import redis_del, redis_get, redis_incr, redis_set, redis_sadd, redis_srem, redis_smembers\nfrom spin_config import config_get\n\n\ndef handle_request(request):\n\n redis_address = config_get(\"redis_address\")\n redis_set(redis_address, \"foo\", b\"bar\")\n value = redis_get(redis_address, \"foo\")\n redis_del(redis_address, [\"testIncr\"])\n redis_incr(redis_address, \"testIncr\")\n\n redis_sadd(redis_address, \"testSets\", [\"hello\", \"world\"])\n content = redis_smembers(redis_address, \"testSets\")\n redis_srem(redis_address, \"testSets\", [\"hello\"])\n\n assert value == b\"bar\", f\"expected \\\"bar\\\", got \\\"{str(value, 'utf-8')}\\\"\"\n\n return Response(200,\n [(\"content-type\", \"text/plain\")],\n bytes(f\"Executed outbound Redis commands: {request.uri}\", \"utf-8\"))","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#configuration"},{"project":"spin","title":"Building Spin Components in Python","subheading":"Building and Running the Application","content":"After we re-build and re-run, again, we can make one final request to our Spin application:$ spin build\n$ spin upThis latest request correctly returns the correct output, in accordance with our Python source code from above:$ curl -i localhost:3000/hello\n\nHTTP/1.1 200 OK\ncontent-type: text/plain\ncontent-length: 40\ndate: Mon, 27 Feb 2023 05:16:03 GMT\n\nExecuted outbound Redis commands: /helloIf we go into our Redis CLI on localhost we can see that the value foo which was set in the Python source code ( redis_set(redis_address, \"foo\", b\"bar\") ) is now correctly set to the value of bar:redis-cli\n127.0.0.1:6379> get foo\n\"bar\"","keywords":"","subsectionKeywords":"","url":"/spin/python-components.md#building-and-running-the-application"},{"project":"spin","title":"Taking Spin for a spin","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedLet's get Spin and take it from nothing to a \"hello world\" application!","keywords":"quickstart","subsectionKeywords":"","url":"/spin/quickstart"},{"project":"spin","title":"Taking Spin for a spin","subheading":"Install Spin","content":"{{ tabs \"os\" }}{{ startTab \"Linux\"}}Download the spin binary using the install.sh script hosted on this site:Then move the spin binary somewhere in your path, so you can run it from anywhere. For example:$ sudo mv ./spin /usr/local/bin/spin{{ blockEnd }}{{ startTab \"macOS\"}}Download the spin binary using the install.sh script hosted on this site:Then move the spin binary somewhere in your path, so you can run it from anywhere. For example:$ sudo mv ./spin /usr/local/bin/spin{{ blockEnd }}{{ startTab \"Windows\"}}Download the Windows binary release of Spin from GitHub.Unzip the binary release and place the spin.exe in your system path.{{ blockEnd }}\n{{ blockEnd }}undefined","keywords":"","subsectionKeywords":"","url":"/spin/quickstart.md#install-spin"},{"project":"spin","title":"Taking Spin for a spin","subheading":"Install the Prerequisites","content":"","keywords":"","subsectionKeywords":"","url":"/spin/quickstart.md#install-the-prerequisites"},{"project":"spin","title":"Taking Spin for a spin","subheading":"Install a Template","content":"The quickest and most convenient way to start a new application is to use a Spin template. Let's install the templates for your preferred language.{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}$ spin templates install --git https://github.com/fermyon/spin --update\nCopying remote template source\nInstalling template redis-rust...\nInstalling template http-rust...\n... other templates omitted ...\n+------------------------------------------------------------------------+\n| Name Description |\n+========================================================================+\n| ... other templates omitted ... |\n| http-rust HTTP request handler using Rust |\n| redis-rust Redis message handler using Rust |\n| ... other templates omitted ... |\n+------------------------------------------------------------------------+Note: The Rust templates are in a repo that contains several other languages; they will all be installed together.{{ blockEnd }}{{ startTab \"TypeScript\" }}$ spin templates install --git https://github.com/fermyon/spin-js-sdk --update\nCopying remote template source\nInstalling template http-js...\nInstalling template http-ts...\n+------------------------------------------------------------------------+\n| Name Description |\n+========================================================================+\n| http-js HTTP request handler using Javascript |\n| http-ts HTTP request handler using Typescript |\n+------------------------------------------------------------------------+{{ blockEnd }}{{ startTab \"Python\" }}$ spin templates install --git https://github.com/fermyon/spin-python-sdk --update\nCopying remote template source\nInstalling template http-py...\n+---------------------------------------------+\n| Name Description |\n+=============================================+\n| http-py HTTP request handler using Python |\n+---------------------------------------------+{{ blockEnd }}{{ startTab \"TinyGo\" }}$ spin templates install --git https://github.com/fermyon/spin --update\nCopying remote template source\nInstalling template redis-go...\nInstalling template http-go...\n+------------------------------------------------------------------------+\n| Name Description |\n+========================================================================+\n| ... other templates omitted ... |\n| http-go HTTP request handler using (Tiny)Go |\n| redis-go Redis message handler using (Tiny)Go |\n| ... other templates omitted ... |\n+------------------------------------------------------------------------+Note: The Go templates are in a repo that contains several other languages; they will all be installed together.{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/quickstart.md#install-a-template"},{"project":"spin","title":"Taking Spin for a spin","subheading":"Install the Tools","content":"Some languages require additional tool support for Wasm.{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}You'll need the wasm32-wasi target for Rust.$ rustup target add wasm32-wasiundefined{{ blockEnd }}{{ startTab \"TypeScript\" }}You'll need the Spin js2wasm plugin.$ spin plugins update\n$ spin plugins install js2wasm --yesundefined{{ blockEnd }}{{ startTab \"Python\" }}You'll need the Spin py2wasm plugin.$ spin plugins update\n$ spin plugins install py2wasm --yesundefined{{ blockEnd }}{{ startTab \"TinyGo\" }}You'll need the TinyGo compiler, as the standard Go compiler does not yet support the WASI standard. See the undefined.undefined{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/quickstart.md#install-the-tools"},{"project":"spin","title":"Taking Spin for a spin","subheading":"Create Your First Application","content":"Now you are ready to create your first Spin application.{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}Use the spin new command and the http-rust template to scaffold a new Spin application.$ spin new\nPick a template to start your application with:\n http-c (HTTP request handler using C and the Zig toolchain)\n http-csharp (HTTP request handler using C# (EXPERIMENTAL))\n http-go (HTTP request handler using (Tiny)Go)\n http-grain (HTTP request handler using Grain)\n> http-rust (HTTP request handler using Rust)\n http-swift (HTTP request handler using SwiftWasm)\n http-zig (HTTP request handler using Zig)\n redis-go (Redis message handler using (Tiny)Go)\n redis-rust (Redis message handler using Rust)\n\nEnter a name for your new application: hello_rust\nProject description: My first Rust Spin application\nHTTP base: /\nHTTP path: /...This command created a directory with the necessary files needed to build and run a Rust Spin application. Change to that directory, and look at the files. It looks very much like a normal Rust library project:$ cd hello_rust\n$ tree\n.\n├── .cargo\n│   └── config.toml\n├── .gitignore\n├── Cargo.toml\n├── spin.toml\n└── src\n └── lib.rsThe additional spin.toml file is the manifest file, which tells Spin what events should trigger what components. In this case our trigger is HTTP, for a Web application, and we have only one component, at the route /.... This is a wildcard that matches any route.spin_manifest_version = \"1\"\nauthors = [\"You \"]\ndescription = \"My first Rust Spin application\"\nname = \"hello_rust\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-rust\"\nsource = \"target/wasm32-wasi/release/hello_rust.wasm\"\nallowed_http_hosts = []\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"cargo build --target wasm32-wasi --release\"This represents a simple Spin HTTP application (triggered by an HTTP request), with\na single component called hello-rust. This component is on the route /..., which is a wildcard\nmeaning it will match any route. When the application gets an HTTP request, Spin will map its route\nto the hello-rust component, and execute the associated hello_rust.wasm\nWebAssembly module.undefinedNow let's have a look at the code. Below is the complete source\ncode for a Spin HTTP component written in Rust — a regular Rust function that\ntakes an HTTP request as a parameter and returns an HTTP response, and it is\nannotated with the http_component macro which identifies it as the entry point\nfor HTTP requests:use anyhow::Result;\nuse spin_sdk::{\n http::{Request, Response},\n http_component,\n};\n\n/// A simple Spin HTTP component.\n#[http_component]\nfn handle_hello_rust(req: Request) -> Result {\n println!(\"{:?}\", req.headers());\n Ok(http::Response::builder()\n .status(200)\n .header(\"foo\", \"bar\")\n .body(Some(\"Hello, Fermyon\".into()))?)\n}{{ blockEnd }}{{ startTab \"TypeScript\"}}Use the spin new command and the http-ts template to scaffold a new Spin application. (If you prefer JavaScript to TypeScript, the http-js template is very similar.)$ spin new\nPick a template to start your application with:\n http-js (HTTP request handler using Javascript)\n> http-ts (HTTP request handler using Typescript)\nEnter a name for your new application: hello_typescript\nProject description: My first TypeScript Spin application\nHTTP base: /\nHTTP path: /...This command created a directory with the necessary files needed to build and run a TypeScript Spin application. Change to that directory, and look at the files. It looks similar to a lot of NPM projects:$ cd hello_typescript\n$ tree\n.\n├── package.json\n├── README.md\n├── spin.toml\n├── src\n│   └── index.ts\n├── tsconfig.json\n└── webpack.config.jsThe additional spin.toml file is the manifest file, which tells Spin what events should trigger what components. In this case our trigger is HTTP, for a Web application, and we have only one component, at the route /.... This is a wildcard that matches any route.spin_manifest_version = \"1\"\nauthors = [\"You \"]\ndescription = \"My first TypeScript Spin application\"\nname = \"hello_typescript\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-typescript\"\nsource = \"target/spin-http-js.wasm\"\nexclude_files = [\"**/node_modules\"]\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"npm run build\"This represents a simple Spin HTTP application (triggered by an HTTP request), with\na single component called hello-typescript. This component is on the route /..., which is a wildcard\nmeaning it will match any route. When the application gets an HTTP request, Spin will map its route\nto the hello-typescript component, and execute the associated spin-http-js.wasm\nWebAssembly module.undefinedNow let's have a look at the code. Below is the complete source\ncode for a Spin HTTP component written in TypeScript — a regular function named handleRequest that\ntakes an HTTP request as a parameter and returns an HTTP response. (The\nJavaScript version looks slightly different, but is still a function with\nthe same signature.) The Spin js2wasm plugin looks for the handleRequest function\nby name when building your application into a Wasm module.import { HandleRequest, HttpRequest, HttpResponse} from \"@fermyon/spin-sdk\"\n\nconst encoder = new TextEncoder()\n\nexport const handleRequest: HandleRequest = async function(request: HttpRequest): Promise {\n return {\n status: 200,\n headers: { \"foo\": \"bar\" },\n body: encoder.encode(\"Hello from TS-SDK\").buffer\n }\n}{{ blockEnd }}{{ startTab \"Python\"}}Use the spin new command and the http-py template to scaffold a new Spin application.$ spin new\nPick a template to start your application with:\n> http-py (HTTP request handler using Python)\nEnter a name for your new application: hello_python\nDescription: My first Python Spin application\nHTTP base: /\nHTTP path: /...This command created a directory with the necessary files needed to build and run a Python Spin application. Change to that directory, and look at the files. It contains a minimal Python application:$ cd hello_python\n$ tree\n.\n├── app.py\n├── Pipfile\n├── README.md\n└── spin.tomlThe additional spin.toml file is the manifest file, which tells Spin what events should trigger what components. In this case our trigger is HTTP, for a Web application, and we have only one component, at the route /.... This is a wildcard that matches any route.spin_manifest_version = \"1\"\nauthors = [\"You \"]\ndescription = \"My first Python Spin application\"\nname = \"hello_python\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-python\"\nsource = \"app.wasm\"\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"spin py2wasm app -o app.wasm\"This represents a simple Spin HTTP application (triggered by an HTTP request), with\na single component called hello-python. This component is on the route /..., which is a wildcard\nmeaning it will match any route. When the application gets an HTTP request, Spin will map its route\nto the hello-python component, and execute the associated app.wasm\nWebAssembly module.undefinedNow let's have a look at the code. Below is the complete source\ncode for a Spin HTTP component written in Python — a regular function named handle_request that\ntakes an HTTP request as a parameter and returns an HTTP response. The Spin py2wasm plugin looks for the handle_request function by name when building your application into a Wasm module.from spin_http import Response\n\ndef handle_request(request):\n return Response(200,\n [(\"content-type\", \"text/plain\")],\n bytes(f\"Hello from the Python SDK\", \"utf-8\")){{ blockEnd }}{{ startTab \"TinyGo\"}}Use the spin new command and the http-go template to scaffold a new Spin application.$ spin new\nPick a template to start your application with:\n http-c (HTTP request handler using C and the Zig toolchain)\n http-empty (HTTP application with no components)\n> http-go (HTTP request handler using (Tiny)Go)\n http-grain (HTTP request handler using Grain)\n http-php (HTTP request handler using PHP)\n http-rust (HTTP request handler using Rust)\nEnter a name for your new application: hello_go\nDescription: My first Go Spin application\nHTTP base: /\nHTTP path: /...This command created a directory with the necessary files needed to build and run a Go Spin application using the TinyGo compiler. Change to that directory, and look at the files. It looks very much like a normal Go project:$ cd hello_go\n$ tree\n.\n├── go.mod\n├── go.sum\n├── main.go\n└── spin.tomlThe additional spin.toml file is the manifest file, which tells Spin what events should trigger what components. In this case our trigger is HTTP, for a Web application, and we have only one component, at the route /.... This is a wildcard that matches any route.spin_manifest_version = \"1\"\nauthors = [\"You \"]\ndescription = \"My first Go Spin application\"\nname = \"hello_go\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"hello-go\"\nsource = \"main.wasm\"\nallowed_http_hosts = []\n[component.trigger]\nroute = \"/...\"\n[component.build]\ncommand = \"tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go\"This represents a simple Spin HTTP application (triggered by an HTTP request), with\na single component called hello-go. This component is on the route /..., which is a wildcard\nmeaning it will match any route. When the application gets an HTTP request, Spin will map its route\nto the hello-go component, and execute the associated main.wasm\nWebAssembly module.undefinedNow let's have a look at the code. Below is the complete source\ncode for a Spin HTTP component written in Go. Notice where the work is done. The\nmain function is empty (and Spin never calls it). Instead, the init function\nsets up a callback, and passes that callback to spinhttp.Handle to register it as\nthe handler for HTTP requests. You can learn more about this structure\nin the undefined.package main\n\nimport (\n \"fmt\"\n \"net/http\"\n\n spinhttp \"github.com/fermyon/spin/sdk/go/http\"\n)\n\nfunc init() {\n spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {\n w.Header().Set(\"Content-Type\", \"text/plain\")\n fmt.Fprintln(w, \"Hello Fermyon!\")\n })\n}\n\nfunc main() {}{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/quickstart.md#create-your-first-application"},{"project":"spin","title":"Taking Spin for a spin","subheading":"Build Your Application","content":"The Spin template creates starter source code. Now you need to turn that into a Wasm module. The template puts build instructions for each component into the manifest. Use the spin build command to run them!{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}$ spin build\nExecuting the build command for component hello-rust: cargo build --target wasm32-wasi --release\n Updating crates.io index\n Updating git repository `https://github.com/fermyon/spin`\n Updating git repository `https://github.com/bytecodealliance/wit-bindgen`\n Compiling anyhow v1.0.69\n Compiling version_check v0.9.4\n # ...\n Compiling spin-sdk v0.10.0 \n Compiling hello-rust v0.1.0 (/home/ivan/testing/start/hello_rust)\n Finished release [optimized] target(s) in 11.94s\nSuccessfully ran the build command for the Spin components.If the build fails, check:undefinedundefinedundefinedIf you would like to know what build command Spin runs for a component, you can find it in the manifest, in the component.build section:[component.build]\ncommand = \"cargo build --target wasm32-wasi --release\"You can always run this command manually; spin build is a shortcut to save you having to remember it.{{ blockEnd }}{{ startTab \"TypeScript\"}}As normal for NPM projects, before you build for the first time, you must run npm install:$ npm install\n\nadded 134 packages, and audited 135 packages in 2s\n\n18 packages are looking for funding\n run `npm fund` for details\n\nfound 0 vulnerabilitiesThen run spin build:$ spin build\nExecuting the build command for component hello-typescript: npm run build\n\n> hello-typescript@1.0.0 build\n> npx webpack --mode=production && mkdir -p target && spin js2wasm -o target/spin-http-js.wasm dist/spin.js\n\nasset spin.js 4.57 KiB [emitted] (name: main)\nruntime modules 670 bytes 3 modules\n./src/index.ts 2.85 KiB [built] [code generated]\nwebpack 5.75.0 compiled successfully in 1026 ms\n\nStarting to build Spin compatible module\nPreinitiating using Wizer\nOptimizing wasm binary using wasm-opt\nSpin compatible module built successfully\nSuccessfully ran the build command for the Spin components.If the build fails, check:undefinedundefinedundefinedIf you would like to know what build command Spin runs for a component, you can find it in the manifest, in the component.build section:[component.build]\ncommand = \"npm run build\"You can always run this command manually; spin build is a shortcut.{{ blockEnd }}{{ startTab \"Python\"}}$ spin build\nExecuting the build command for component hello-python: spin py2wasm app -o app.wasm\nSpin-compatible module built successfully\nSuccessfully ran the build command for the Spin components.If the build fails, check:undefinedundefinedIf you would like to know what build command Spin runs for a component, you can find it in the manifest, in the component.build section:[component.build]\ncommand = \"spin py2wasm app -o app.wasm\"You can always run this command manually; spin build is a shortcut.{{ blockEnd }}{{ startTab \"TinyGo\"}}$ spin build\nExecuting the build command for component hello-go: tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go\ngo: downloading github.com/fermyon/spin/sdk/go v0.10.0\nSuccessfully ran the build command for the Spin components.If the build fails, check:undefinedundefinedundefinedIf you would like to know what build command Spin runs for a component, you can find it in the manifest, in the component.build section:[component.build]\ncommand = \"tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go\"You can always run this command manually; spin build is a shortcut to save you having to remember it.{{ blockEnd }}{{ blockEnd }}undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/quickstart.md#build-your-application"},{"project":"spin","title":"Taking Spin for a spin","subheading":"Run Your Application","content":"Now that you have created the application and built the component, you can undefined\nthe application (pun intended):$ spin up\nServing http://127.0.0.1:3000\nAvailable Routes:\n hello-typescript: http://127.0.0.1:3000 (wildcard)If you would like to see what Spin is doing under the hood, set the RUST_LOG environment variable for detailed logs, before running spin up:$ export RUST_LOG=spin=traceundefinedSpin instantiates all components from the application manifest, and\ncreates the router configuration for the HTTP trigger according to the routes in the manifest. The\ncomponent can now be invoked by making requests to http://localhost:3000/\n(or any path under that, since it's a wildcard):$ curl -i localhost:3000\nHTTP/1.1 200 OK\nfoo: bar\ncontent-length: 14\ndate: Thu, 02 Mar 2023 00:05:42 GMT\n\nHello, FermyonCongratulations! You just created, built and ran your first Spin application!","keywords":"","subsectionKeywords":"","url":"/spin/quickstart.md#run-your-application"},{"project":"spin","title":"Taking Spin for a spin","subheading":"Next Steps","content":"undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/quickstart.md#next-steps"},{"project":"spin","title":"Relational Database Storage","subheading":"","content":"undefinedSpin provides an interface for you to access relational (SQL) databases, specifically MySQL and PostgreSQL.{{ details \"Why do I need a Spin interface? Why can't I just use my language's database libraries?\" \"The current version of the WebAssembly System Interface (WASI) doesn't provide a sockets interface, so database libraries that depend on sockets can't be built to Wasm. The Spin interface means Wasm modules can bypass this limitation by asking Spin to make the database connection on their behalf.\" }}","subsectionKeywords":"","url":"/spin/rdbms-storage"},{"project":"spin","title":"Relational Database Storage","subheading":"Using MySQL and PostgreSQL From Applications","content":"The Spin SDK surfaces the Spin MySQL and PostgreSQL interfaces to your language. The set of operations is the same across both databases:| Operation | Parameters | Returns | Behavior |\n|------------|-------------------------------------|---------------------|----------|\n| query | address, statement, SQL parameters | database records | Runs the specified statement against the database, returning the query results as a set of rows. |\n| execute | address, statement, SQL parameters | integer (not MySQL) | Runs the specified statement against the database, returning the number of rows modified by the statement. (MySQL does not return the modified row count.) |The exact detail of calling these operations from your application depends on your language:{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}MySQL functions are available in the spin_sdk::mysql module, and PostgreSQL functions in the spin_sdk::pg module. The function names match the operations above. This example shows MySQL:use spin_sdk::mysql::{self, Decode, ParameterValue};\n\nlet params = vec![ParameterValue::Int32(id)];\nlet rowset = mysql::query(&address, \"SELECT id, name FROM pets WHERE id = ?\", ¶ms)?;\n\nmatch rowset.rows.first() {\n None => /* no rows matched query */,\n Some(row) => {\n let name = String::decode(&row[1])?;\n }\n}undefinedundefinedundefinedundefinedundefinedundefinedYou can find complete examples for using relational databases in the Spin repository on GitHub (undefined, undefined).{{ blockEnd }}{{ startTab \"TypeScript\"}}The JavaScript/TypeScript SDK doesn't surface the MySQL or PostgreSQL APIs. However, you can use hosted relational database services that are accessible over HTTP. For an example, see the JavaScript SDK repository on GitHub (undefined, undefined).{{ blockEnd }}{{ startTab \"Python\"}}The Python SDK doesn't currently surface the MySQL or PostgreSQL APIs.{{ blockEnd }}{{ startTab \"TinyGo\"}}The Go SDK doesn't currently surface the MySQL or PostgreSQL APIs.{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/rdbms-storage.md#using-mysql-and-postgresql-from-applications"},{"project":"spin","title":"Redis Storage","subheading":"","content":"undefinedSpin provides an interface for you to read and write the Redis key/value store, and to publish Redis pub-sub messages.{{ details \"Why do I need a Spin interface? Why can't I just use my language's Redis library?\" \"The current version of the WebAssembly System Interface (WASI) doesn't provide a sockets interface, so Redis libraries that depend on sockets can't be built to Wasm. The Spin interface means Wasm modules can bypass this limitation by asking Spin to make the Redis connection on their behalf.\" }}","subsectionKeywords":"","url":"/spin/redis-outbound"},{"project":"spin","title":"Redis Storage","subheading":"Using Redis From Applications","content":"The Spin SDK surfaces the Spin Redis interface to your language. The set of operations is common across all SDKs:| Operation | Parameters | Returns | Behavior |\n|------------|------------|---------|----------|\n| Single value operations |\n| get | address, key | bytes | Returns the value of key. If the key does not exist, returns a zero-length array. |\n| set | address, key, bytes | - | Sets the value of key, overwriting any existing value. |\n| incr | address, key | integer | Increments the value at key by 1. If the key does not exist, its value is set to 0 first (and immediately incremented to 1). If the current value isn't an integer, or a string that represents an integer, it errors and the value is not changed. |\n| del | address, list of keys | - | Removes the specified keys. Keys that don't exist are ignored (they do undefined cause an error). |\n| Set value operations |\n| sadd | address, key, list of strings | integer | Adds the strings to the set of values of key, and returns the number of newly added values. If the key does not exist, its value is set to the set of values |\n| smembers | address, key | list of strings | Returns the set of values of key. if the key does not exist, this is an empty set. |\n| srem | address, key, list of strings | integer | Removes the strings from the set of values of key, and returns the number of newly removed values. If the key does not exist, this does nothing. |\n| Pub-sub operations |\n| publish | address, channel, bytes | - | Publishes a message to the specified channel, with the specified payload bytes. |\n| General operations |\n| execute | address, command, list of argument values | list of results | Executes the specified command with the specified arguments. This is a general-purpose 'escape hatch' if you need a command that isn't covered by the built-in operations. |The exact detail of calling these operations from your application depends on your language:{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}Redis functions are available in the spin_sdk::redis module. The function names match the operations above. For example:use spin_sdk::redis;\n\nlet value = redis::get(&address, &key)?;undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedYou can find a complete example for using outbound Redis from an HTTP component in the undefined.{{ blockEnd }}{{ startTab \"TypeScript\"}}Redis functions are available on the spinSdk.redis object. The function names match the operations above. For example:const value = spinSdk.redis.get(address, key);undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedYou can find a complete example for using outbound Redis from an HTTP component in the undefined.{{ blockEnd }}{{ startTab \"Python\"}}Redis functions are available in the spin_redis module. The function names are prefixed redis_. For example:from spin_redis import redis_get\n\nvalue = redis_get(address, key)undefinedundefinedundefinedundefinedundefinedundefinedYou can find a complete example for using outbound Redis from an HTTP component in the undefined.{{ blockEnd }}{{ startTab \"TinyGo\"}}Redis functions are available in the github.com/fermyon/spin/sdk/go/redis package. The function names are TitleCased. For example:import (\n\t\"github.com/fermyon/spin/sdk/go/redis\"\n)\n\npayload, err := redis.Get(address, key)undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedYou can find a complete example for using outbound Redis from an HTTP component in the undefined.{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/redis-outbound.md#using-redis-from-applications"},{"project":"spin","title":"The Spin Redis trigger","subheading":"","content":"undefinedundefinedundefinedundefinedPub-sub (publish-subscribe) messaging is a popular architecture for asynchronous message processing. Spin has built-in support to creating and running applications in response to messages on undefined.The Redis trigger in Spin subscribes to messages from a given Redis instance, and dispatches those messages to components for handling.undefined","subsectionKeywords":"","url":"/spin/redis-trigger"},{"project":"spin","title":"The Spin Redis trigger","subheading":"Specifying an Application as Redis","content":"Every Spin application has a trigger specified in the manifest, which declares the type of events it responds to.\nFor Redis applications, the application trigger has type = \"redis\":# spin.toml\ntrigger = { type = \"redis\", address = \"redis://localhost:6379\" }The Redis trigger also requires an address field. This is the address of the Redis instance to monitor. Specify it using the redis: URL scheme.undefinedBy default, Spin does not authenticate to Redis. You can work around this by providing a password in the redis:// URL. For example: address = \"redis://:p4ssw0rd@localhost:6379\"undefinedundefinedIn addition, each component must have Redis-specific configuration in its [component.trigger] table.","keywords":"","subsectionKeywords":"","url":"/spin/redis-trigger.md#specifying-an-application-as-redis"},{"project":"spin","title":"The Spin Redis trigger","subheading":"Mapping a Channel to a Component","content":"Each component handles one channel, specified in the channel field of the component trigger table. For example, Spin will trigger this component when it receives a message on the purchaseorders channel:[component.trigger]\nchannel = \"purchaseorders\"undefined","keywords":"","subsectionKeywords":"","url":"/spin/redis-trigger.md#mapping-a-channel-to-a-component"},{"project":"spin","title":"The Spin Redis trigger","subheading":"Redis Components","content":"Spin runs Redis components using the undefined. In this model, the Wasm module exports a well-known function that Spin calls to handle the Redis message.","keywords":"","subsectionKeywords":"","url":"/spin/redis-trigger.md#redis-components"},{"project":"spin","title":"The Spin Redis trigger","subheading":"The Message Handler","content":"The exact signature of the Redis handler, and how a function is identified to be exported as the handler, will depend on your language.{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}In Rust, the handler is identified by the #[spin_sdk::redis_component] attribute. It takes a bytes::Bytes, representing the raw payload of the Redis message, and returns an anyhow::Result indicating success or an error with details. This example just logs the payload as a string:use anyhow::Result;\nuse bytes::Bytes;\nuse spin_sdk::redis_component;\nuse std::str::from_utf8;\n\n/// A simple Spin Redis component.\n#[redis_component]\nfn on_message(message: Bytes) -> Result<()> {\n println!(\"{}\", from_utf8(&message)?);\n Ok(())\n}{{ blockEnd }}{{ startTab \"TypeScript\"}}The JavaScript/TypeScript SDK doesn't currently support Redis components. Please undefined if this is important to you.{{ blockEnd }}{{ startTab \"Python\"}}The Python SDK doesn't currently support Redis components. Please undefined if this is important to you.{{ blockEnd }}{{ startTab \"TinyGo\"}}In Go, you register the handler as a callback in your program's init function. Call redis.Handle (from the Spin SDK redis package), passing your handler as the sole argument. Your handler takes a single byte slice ([]byte) argument, and may return an error or nil.undefinedThis example just logs the payload as a string:package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/fermyon/spin/sdk/go/redis\"\n)\n\nfunc init() {\n\tredis.Handle(func(payload []byte) error {\n\t\tfmt.Println(string(payload))\n\t\treturn nil\n\t})\n}\n\nfunc main() {}{{ blockEnd }}{{ blockEnd }}","keywords":"","subsectionKeywords":"","url":"/spin/redis-trigger.md#the-message-handler"},{"project":"spin","title":"The Spin Redis trigger","subheading":"Inside Redis Components","content":"For the most part, you'll build Redis component modules using a language SDK (see the Language Guides section), such as a Rust crate or Go package. If you're interested in what happens inside the SDK, or want to implement Redis components in another language, read on!undefinedThe Redis component interface is defined using a WebAssembly Interface (WIT) file. (undefined). You can find the latest WITs for Spin Redis components at undefined.The core Redis types are defined in undefined, though note that not all of these are used in the pub-sub Redis trigger:// General purpose error.\nenum error {\n success,\n error,\n}\n\n// The message payload.\ntype payload = list\n\n// Remaining types are not used in the triggerundefinedThe entry point for Spin Redis components is then defined in undefined:// wit/ephemeral/spin-redis.wit\n\nuse * from redis-types\n\n// The entry point for a Redis handler.\nhandle-redis-message: func(message: payload) -> expectedThis is the function signature that all Redis components must implement, and\nwhich is used by Spin when instantiating and invoking the component.This interface (spin-redis.wit) can be directly used together with the\nundefined\nto build a component that Spin can invoke.This is exactly how Spin SDKs, such as the undefined and undefined SDKs, are built.\nAs more languages add support for the component model, we plan to add support for them in the same way.undefined","keywords":"","subsectionKeywords":"","url":"/spin/redis-trigger.md#inside-redis-components"},{"project":"spin","title":"Running Spin Applications","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedOnce you have created and built your application, it's ready to run. To run an application, use the spin up command.","subsectionKeywords":"","url":"/spin/running-apps"},{"project":"spin","title":"Running Spin Applications","subheading":"Specifying the Application to Run","content":"By default, spin up looks for a file named spin.toml in the current directory.If your manifest is named something different, or isn't in your current directory, use the -f (--from) flag. You also use -f to run remote applications.| -f Value | spin up Behavior |\n|---------------------------------|---------------------|\n| File name: -f demo.toml | Runs the specified manifest file |\n| Directory name: -f demo/ | Looks for a spin.toml file in that directory and runs that |\n| Registry reference: -f ghcr.io/fermyon/example:v1 | Pulls the application from the registry and runs that |undefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/running-apps.md#specifying-the-application-to-run"},{"project":"spin","title":"Running Spin Applications","subheading":"Testing HTTP Applications","content":"By default, HTTP applications listen on localhost:3000. You can override this with the --listen option. Spin prints links to the application components to make it easy to open them in the browser or copy them to curl commands for testing.","keywords":"","subsectionKeywords":"","url":"/spin/running-apps.md#testing-http-applications"},{"project":"spin","title":"Running Spin Applications","subheading":"Application Output","content":"By default, Spin prints application output, and any of its own error messages, to the console.To hide application output, pass the --quiet flag:$ spin up --quietTo limit application output to specific components, pass the --follow flag:$ spin up --follow cart --follow cart-api","keywords":"","subsectionKeywords":"","url":"/spin/running-apps.md#application-output"},{"project":"spin","title":"Running Spin Applications","subheading":"Persistent Logs","content":"By default:undefinedundefinedTo control logging, pass the --log-dir flag. The logs will be saved to the specified directory (no matter whether the application is local or remote).$ spin up --log-dir ~/dev/bugbash","keywords":"","subsectionKeywords":"","url":"/spin/running-apps.md#persistent-logs"},{"project":"spin","title":"Running Spin Applications","subheading":"Trigger-Specific Options","content":"Some trigger types support additional spin up flags. For example, HTTP applications can have a --listen flag to specify an address and port to listen on. See the undefined and undefined pages for more details.","keywords":"","subsectionKeywords":"","url":"/spin/running-apps.md#trigger-specific-options"},{"project":"spin","title":"Running Spin Applications","subheading":"Next Steps","content":"undefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/running-apps.md#next-steps"},{"project":"spin","title":"Building Spin components in Rust","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedSpin aims to have best-in-class support for building components in Rust, and\nwriting such components should be familiar for Rust developers.undefinedundefinedundefined","subsectionKeywords":"","url":"/spin/rust-components"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Prerequisites","content":"","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#prerequisites"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Install the Templates","content":"You don't need the Spin Rust templates to work on Rust components, but they speed up creating new applications and components. You can install them as follows:$ spin templates install --git https://github.com/fermyon/spin --update\nCopying remote template source\nInstalling template redis-rust...\nInstalling template http-rust...\n... other templates omitted ...\n+------------------------------------------------------------------------+\n| Name Description |\n+========================================================================+\n| ... other templates omitted ... |\n| http-rust HTTP request handler using Rust |\n| redis-rust Redis message handler using Rust |\n| ... other templates omitted ... |\n+------------------------------------------------------------------------+Note: The Rust templates are in a repo that contains several other languages; they will all be installed together.","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#install-the-templates"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Install the Tools","content":"To build Spin components, you'll need the wasm32-wasi target for Rust.$ rustup target add wasm32-wasiundefined","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#install-the-tools"},{"project":"spin","title":"Building Spin components in Rust","subheading":"HTTP Components","content":"In Spin, HTTP components are triggered by the occurrence of an HTTP request, and\nmust return an HTTP response at the end of their execution. Components can be\nbuilt in any language that compiles to WASI, but Rust has improved support\nfor writing Spin components with the Spin Rust SDK.undefinedBuilding a Spin HTTP component using the Rust SDK means writing a single function\nthat takes an HTTP request as a parameter, and returns an HTTP response — below\nis a complete implementation for such a component:use anyhow::Result;\nuse spin_sdk::{\n http::{Request, Response},\n http_component,\n};\n\n/// A simple Spin HTTP component.\n#[http_component]\nfn hello_world(req: Request) -> Result {\n println!(\"{:?}\", req);\n Ok(http::Response::builder()\n .status(200)\n .header(\"foo\", \"bar\")\n .body(Some(\"Hello, Fermyon!\".into()))?)\n}The important things to note in the implementation above:undefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#http-components"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Redis Components","content":"Besides the HTTP trigger, Spin has built-in support for a Redis trigger —\nwhich will connect to a Redis instance and will execute Spin components for\nnew messages on the configured channels.undefinedWriting a Redis component in Rust also takes advantage of the SDK:use anyhow::Result;\nuse bytes::Bytes;\nuse spin_sdk::redis_component;\n\n/// A simple Spin Redis component.\n#[redis_component]\nfn on_message(message: Bytes) -> Result<()> {\n println!(\"{}\", std::str::from_utf8(&message)?);\n Ok(())\n}undefinedundefinedundefinedThe component can be built with Cargo by executing:$ cargo build --target wasm32-wasi --releaseThe manifest for a Redis application must contain the address of the Redis\ninstance the trigger must connect to:spin_manifest_version = \"1\"\nname = \"spin-redis\"\ntrigger = { type = \"redis\", address = \"redis://localhost:6379\" }\nversion = \"0.1.0\"\n\n[[component]]\nid = \"echo-message\"\nsource = \"target/wasm32-wasi/release/spinredis.wasm\"\n[component.trigger]\nchannel = \"messages\"This application will connect to redis://localhost:6379, and for every new\nmessage on the messages channel, the echo-message component will be executed:# first, start redis-server on the default port 6379\n$ redis-server --port 6379\n# then, start the Spin application\n$ spin up --file spin.toml\n# the application log file will output the following\nINFO spin_redis_engine: Connecting to Redis server at redis://localhost:6379\nINFO spin_redis_engine: Subscribed component 0 (echo-message) to channel: messagesFor every new message on the messages channel:$ redis-cli\n127.0.0.1:6379> publish messages \"Hello, there!\"Spin will instantiate and execute the component we just built, which will emit the println! message to the application log file:INFO spin_redis_engine: Received message on channel \"messages\"\nHello, there!undefined","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#redis-components"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Sending Outbound HTTP Requests","content":"If allowed, Spin components can send outbound HTTP requests.\nLet's see an example of a component that makes a request to\nundefined and\ninserts a custom header into the response before returning:#[http_component]\nfn hello_world(_req: Request) -> Result {\n let mut res = spin_sdk::http::send(\n http::Request::builder()\n .method(\"GET\")\n .uri(\"https://some-random-api.ml/facts/dog\")\n .body(None)?,\n )?;\n\n res.headers_mut()\n .insert(http::header::SERVER, \"spin/0.1.0\".try_into()?);\n\n Ok(res)\n}undefinedBefore we can execute this component, we need to add the some-random-api.ml\ndomain to the application manifest allowed_http_hosts list containing the list of\ndomains the component is allowed to make HTTP requests to:# spin.toml\nspin_manifest_version = \"1\"\nname = \"spin-hello-world\"\ntrigger = { type = \"http\", base = \"/\" }\nversion = \"1.0.0\"\n\n[[component]]\nid = \"hello\"\nsource = \"target/wasm32-wasi/release/spinhelloworld.wasm\"\nallowed_http_hosts = [ \"some-random-api.ml\" ]\n[component.trigger]\nroute = \"/outbound\"Running the application using spin up --file spin.toml will start the HTTP\nlistener locally (by default on localhost:3000), and our component can\nnow receive requests in route /outbound:$ curl -i localhost:3000/outbound\nHTTP/1.1 200 OK\ndate: Fri, 18 Mar 2022 03:54:36 GMT\ncontent-type: application/json; charset=utf-8\ncontent-length: 185\nserver: spin/0.1.0\n\n{\"fact\":\"It's rumored that, at the end of the Beatles song, \n\\\"A Day in the Life,\\\" Paul McCartney recorded an ultrasonic whistle, \naudible only to dogs, just for his Shetland sheepdog.\"}undefinedundefinedWe just built a WebAssembly component that sends an HTTP request to another\nservice, manipulates that result, then responds to the original request.\nThis can be the basis for building components that communicate with external\ndatabases or storage accounts, or even more specialized components like HTTP\nproxies or URL shorteners.","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#sending-outbound-http-requests"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Storing Data in Redis From Rust Components","content":"Using the Spin's Rust SDK, you can use the Redis key/value store and to publish\nmessages to Redis channels. This can be used from both HTTP and Redis triggered\ncomponents.Let's see how we can use the Rust SDK to connect to Redis:use anyhow::{anyhow, Result};\nuse spin_sdk::{\n http::{internal_server_error, Request, Response},\n http_component, redis,\n};\n\n// The environment variable set in `spin.toml` that points to the\n// address of the Redis server that the component will publish\n// a message to.\nconst REDIS_ADDRESS_ENV: &str = \"REDIS_ADDRESS\";\n\n// The environment variable set in `spin.toml` that specifies\n// the Redis channel that the component will publish to.\nconst REDIS_CHANNEL_ENV: &str = \"REDIS_CHANNEL\";\n\n/// This HTTP component demonstrates fetching a value from Redis\n/// by key, setting a key with a value, and publishing a message\n/// to a Redis channel. The component is triggered by an HTTP\n/// request served on the route configured in the `spin.toml`.\n#[http_component]\n\nfn publish(_req: Request) -> Result {\n let address = std::env::var(REDIS_ADDRESS_ENV)?;\n let channel = std::env::var(REDIS_CHANNEL_ENV)?;\n\n // Get the message to publish from the Redis key \"mykey\"\n let payload = redis::get(&address, \"mykey\").map_err(|_| anyhow!(\"Error querying Redis\"))?;\n\n // Set the Redis key \"spin-example\" to value \"Eureka!\"\n redis::set(&address, \"spin-example\", &b\"Eureka!\"[..])\n .map_err(|_| anyhow!(\"Error executing Redis set command\"))?;\n\n // Set the Redis key \"int-key\" to value 0\n redis::set(&address, \"int-key\", format!(\"{:x}\", 0).as_bytes())\n .map_err(|_| anyhow!(\"Error executing Redis set command\"))?;\n let int_value = redis::incr(&address, \"int-key\")\n .map_err(|_| anyhow!(\"Error executing Redis incr command\",))?;\n assert_eq!(int_value, 1);\n\n // Publish to Redis\n match redis::publish(&address, &channel, &payload) {\n Ok(()) => Ok(http::Response::builder().status(200).body(None)?),\n Err(_e) => internal_server_error(),\n }\n}This HTTP component demonstrates fetching a value from Redis by key, setting a\nkey with a value, and publishing a message to a Redis channel. The component is\ntriggered by an HTTP request served on the route configured in the spin.toml:[[component]]\nenvironment = { REDIS_ADDRESS = \"redis://127.0.0.1:6379\", REDIS_CHANNEL = \"messages\" }\n[component.trigger]\nroute = \"/publish\"This HTTP component can be paired with a Redis component, triggered on new\nmessages on the messages Redis channel.undefined","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#storing-data-in-redis-from-rust-components"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Storing Data in the Spin Key-Value Store","content":"Spin has a key-value store built in. For information about using it from Rust, see undefined.","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#storing-data-in-the-spin-key-value-store"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Storing Data in Relational Databases","content":"Spin provides clients for MySQL and PostgreSQL. For information about using them from Rust, see undefined.","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#storing-data-in-relational-databases"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Using External Crates in Rust Components","content":"In Rust, Spin components are regular libraries that contain a function\nannotated using the http_component macro, compiled to the\nundefined.\nThis means that any undefined that compiles to wasm32-wasi can\nbe used when implementing the component.","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#using-external-crates-in-rust-components"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Troubleshooting","content":"If you bump into issues building and running your Rust component, here are some common causes of problems:undefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#troubleshooting"},{"project":"spin","title":"Building Spin components in Rust","subheading":"Manually Creating New Projects With Cargo","content":"The recommended way of creating new Spin projects is by starting from a template.\nThis section shows how to manually create a new project with Cargo.When creating a new Spin project with Cargo, you should use the --lib flag:$ cargo init --libA Cargo.toml with standard Spin dependencies looks like this:[package]\nname = \"your-app\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\n# Required to have a `cdylib` (dynamic library) to produce a Wasm module.\ncrate-type = [ \"cdylib\" ]\n\n[dependencies]\n# Useful crate to handle errors.\nanyhow = \"1\"\n# Crate to simplify working with bytes.\nbytes = \"1\"\n# General-purpose crate with common HTTP types.\nhttp = \"0.2\"\n# The Spin SDK.\nspin-sdk = { git = \"https://github.com/fermyon/spin\" }\n# Crate that generates Rust Wasm bindings from a WebAssembly interface.\nwit-bindgen-rust = { git = \"https://github.com/bytecodealliance/wit-bindgen\", rev = \"cb871cfa1ee460b51eb1d144b175b9aab9c50aba\" }undefined","keywords":"","subsectionKeywords":"","url":"/spin/rust-components.md#manually-creating-new-projects-with-cargo"},{"project":"spin","title":"Hosting Spin Apps in Open Container Initiative (OCI) Registries","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined","subsectionKeywords":"","url":"/spin/spin-oci"},{"project":"spin","title":"Hosting Spin Apps in Open Container Initiative (OCI) Registries","subheading":"Spin Open Container Initiative (OCI) Support","content":"With Spin's Open Container Initiative (OCI) support, you can package and save your Spin application as an OCI artifact in a registry like undefined or undefined and then run your Spin app from these registries.","keywords":"","subsectionKeywords":"","url":"/spin/spin-oci.md#spin-open-container-initiative-oci-support"},{"project":"spin","title":"Hosting Spin Apps in Open Container Initiative (OCI) Registries","subheading":"Prerequisites","content":"First, follow undefined to ensure you have the latest version of Spin installed (this tutorial refers to Spin 1.0 and above). You can check the Spin version using the following command:$ spin --version","keywords":"","subsectionKeywords":"","url":"/spin/spin-oci.md#prerequisites"},{"project":"spin","title":"Hosting Spin Apps in Open Container Initiative (OCI) Registries","subheading":"Publishing and Running Spin Applications Using Registries (Video)","content":"The following video shows you how to push a Spin app to GHCR, and then run that artifact with Spin or with Docker. The video also contains additional information about signing and verifying your GHCR artifacts.The rest of this page shows you how to use OCI-compliant GHCR artifacts locally with Spin.","keywords":"","subsectionKeywords":"","url":"/spin/spin-oci.md#publishing-and-running-spin-applications-using-registries-video"},{"project":"spin","title":"Hosting Spin Apps in Open Container Initiative (OCI) Registries","subheading":"Set Up Your GHCR Instance","content":"To use a GHCR instance, you need to set up authentication. Follow undefined to generate a personal access token and then use it to sign into your GHCR. You should see the following message to confirm your login attempt to GHCR was successful:> Login Succeed","keywords":"","subsectionKeywords":"","url":"/spin/spin-oci.md#set-up-your-ghcr-instance"},{"project":"spin","title":"Hosting Spin Apps in Open Container Initiative (OCI) Registries","subheading":"Push a Spin App to GHCR","content":"Let's use this full-stack undefined to walk through this tutorial. If you have a Spin app already feel free to navigate to that directory and skip the step below.Fork and clone the app undefined:$ git clone https://github.com/USERNAME/spin-react-fullstack.gitNow, switch to that directory and rebuild the application:$ cd spin-react-fullstack\n$ spin buildNow we're ready to push the application. Run the spin registry push command to push your application to the registry:$ spin registry push ghcr.io/USERNAME/spin-react-fullstack:v1undefinedYou now have a Spin application stored in your registry. You can see the artifact under packages in the undefined.","keywords":"","subsectionKeywords":"","url":"/spin/spin-oci.md#push-a-spin-app-to-ghcr"},{"project":"spin","title":"Hosting Spin Apps in Open Container Initiative (OCI) Registries","subheading":"Pull a Spin App From GHCR","content":"Now that we've successfully pushed a Spin app, let's see if we can pull it. To do so, run the following command:$ spin registry pull ghcr.io/USERNAME/spin-react-fullstack:v1","keywords":"","subsectionKeywords":"","url":"/spin/spin-oci.md#pull-a-spin-app-from-ghcr"},{"project":"spin","title":"Hosting Spin Apps in Open Container Initiative (OCI) Registries","subheading":"Run a Spin App From GHCR","content":"Lastly, let's run this Spin application:$ spin up -f ghcr.io/USERNAME/spin-react-fullstack:v1","keywords":"","subsectionKeywords":"","url":"/spin/spin-oci.md#run-a-spin-app-from-ghcr"},{"project":"spin","title":"Hosting Spin Apps in Open Container Initiative (OCI) Registries","subheading":"Conclusion","content":"Congratulations on completing this tutorial! You have now successfully built, pushed, pulled, and run a Spin app using GHCR. Behind the scenes, Spin uses undefined project to distribute Spin apps across container registries. To learn more about how this feature works, take a look at undefined and undefined.","keywords":"","subsectionKeywords":"","url":"/spin/spin-oci.md#conclusion"},{"project":"spin","title":"Hosting Spin Apps in Open Container Initiative (OCI) Registries","subheading":"Next Steps","content":"undefined","keywords":"","subsectionKeywords":"","url":"/spin/spin-oci.md#next-steps"},{"project":"spin","title":"Creating Spin templates","subheading":"","content":"undefinedundefinedundefinedundefinedSpin templates allow a Spin developer to quickly create the skeleton of an\napplication or component, ready for the application logic to be filled in.A template consists of two directories, content and metadata.undefinedundefinedFor examples of the directory contents, see the templates directory in the\nundefined.Templates must always be shared in a templates directory. This allows the\ninstaller to locate them in repos that contain other content.","subsectionKeywords":"","url":"/spin/template-authoring"},{"project":"spin","title":"Creating Spin templates","subheading":"Authoring the Content","content":"Copy all the files that you want to be copied as part of the template into\nthe content directory. If you do nothing more, they will be copied\nverbatim. Often, though, you'll want to allow the user to put their own\nvalues in - for example, a project name, or an HTTP route.To do this, replace the text you want the user to be able to substitute\nwith an expression of the form {{parameter-name}}, where parameter-name\nis an identifier of your choice. undefined - see below.You can reuse a parameter in more than one place - it will be prompted only once and will get the same value in each place.You can also transform the user value by specifying a filter after a bar:\n{{parameter-name | filter-name}}. This is particularly useful when you\nwant to conform to specific language conventions. The following filters\nare supported:| Name | Effect |\n|---------------|--------|\n| kebab_case | Transforms input into kebab case, e.g. My Application to my-application |\n| snake_case | Transforms input into snake case, e.g. My Application to my_application |\n| pascal_case | Transforms input into Pascal case, e.g. my appplication to MyApplication |","keywords":"","subsectionKeywords":"","url":"/spin/template-authoring.md#authoring-the-content"},{"project":"spin","title":"Creating Spin templates","subheading":"Authoring the Manifest","content":"The template manifest is a TOML file. It must be named spin-template.toml:manifest_version = \"1\"\nid = \"my-application\"\ndescription = \"An application\"\ntags = [\"my-tag\"]\n\n[parameters]\n# Example parameter\nproject-name = { type = \"string\", prompt = \"Project name\" }undefinedundefinedundefinedundefinedThe parameters table is where you list the placeholders that you edited\ninto your content for the user to substitute. You should include an entry\nfor each parameter. The key is the parameter name, and the value a JSON\ndocument that contains at minimum a type and prompt. type must\ncurrently be string. prompt is displayed when prompting the user\nfor the value to substitute.The document may also have a default, which will be displayed to the user\nand can be accepted by pressing Enter. It may also specify constraints\non what the user is allowed to enter. The following constraints are\nsupported:| Key | Value and usage |\n|---------------|-----------------|\n| pattern | A regular expression. The user input must match the regular expression to be accepted. |","keywords":"","subsectionKeywords":"","url":"/spin/template-authoring.md#authoring-the-manifest"},{"project":"spin","title":"Creating Spin templates","subheading":"Supporting spin add","content":"The spin add command lets users add your template as a new component in\nan existing application. If you'd like to support this, you'll need to\nadd a few items to your metadata.undefinedundefined| Key | Value and usage |\n|-----------------|-----------------|\n| snippets | A subtable with an entry named component, whose value is the name of the file containing the component manifest template. (Don't include the snippets directory prefix - Spin knows to look in the snippets directory.) |\n| skip_files | Optional array of content files that should undefined be copied when running in \"add component\" mode. For example, if your template contains a spin.toml file, you should use this setting to exclude that, because you want to add a new entry to the existing file, not overwrite it. |\n| skip_parameters | Optional array of parameters that Spin should undefined prompt for when running in \"add component\" mode. For example, the HTTP templates don't prompt for the base path, because that's defined at the application level, not set on an individual component. |Here is an example add_component table from a HTTP template:[add_component]\nskip_files = [\"spin.toml\"]\nskip_parameters = [\"http-base\"]\n[add_component.snippets]\ncomponent = \"component.txt\"undefined","keywords":"","subsectionKeywords":"","url":"/spin/template-authoring.md#supporting-spin-add"},{"project":"spin","title":"Creating Spin templates","subheading":"Hosting Templates in Git","content":"You can publish templates in a Git repo. The templates must be in the /templates\ndirectory, with a subdirectory per template.When a user installs templates from your repo, by default Spin looks for a tag\nto identify a compatible version of the templates. This tag is of the\nform spin/templates/vX.Y, where X is the major version, and Y the minor\nversion, of the user's copy of Spin. For example, if the user is on\nSpin 0.3.1, templates will be installed from spin/templates/v0.3. If this\ntag does not exist, Spin installs templates from HEAD.","keywords":"","subsectionKeywords":"","url":"/spin/template-authoring.md#hosting-templates-in-git"},{"project":"spin","title":"Building a URL shortener with Spin","subheading":"","content":"undefinedundefinedundefined","subsectionKeywords":"","url":"/spin/url-shortener"},{"project":"spin","title":"Building a URL shortener with Spin","subheading":"A Simple URL Shortener Built With Spin","content":"This tutorial will walk you through building a Spin component that\nredirects short URLs to their configured destinations.\nIn essence, this is a simple HTTP component that returns a response that contains\nredirect information based on the user-defined routes.This is an evolving tutorial. As Spin allows building more complex components\n(through supporting access to services like databases), this tutorial will be\nupdated to reflect that.","keywords":"","subsectionKeywords":"","url":"/spin/url-shortener.md#a-simple-url-shortener-built-with-spin"},{"project":"spin","title":"Building a URL shortener with Spin","subheading":"Here Is One We Prepared Earlier","content":"The complete implementation for this tutorial undefined.First, our URL shortener allows users to configure their own final URLs —\ncurrently, that is done through a configuration file that contains multiple\n[[route]] entries, each containing the shortened path as source, and\nthe destination URL:[[route]]\nsource = \"/spin\"\ndestination = \"https://github.com/fermyon/spin\"\n\n[[route]]\nsource = \"/hype\"\ndestination = \"https://www.fermyon.com/blog/how-to-think-about-wasm\"Whenever a request for https:///spin is sent, our component will\nredirect to https://github.com/fermyon/spin. Now that we have a basic\nunderstanding of how the component should behave, let's see how to implement it\nusing Spin.First, we start with undefined:/// A Spin HTTP component that redirects requests \n/// based on the router configuration.\n#[http_component]\nfn redirect(req: Request) -> Result {\n let router = Router::default()?;\n router.redirect(req)\n}All the component does is create a new router based on the default configuration,\nthen use it to redirect the request. Let's see how the router is defined:#[derive(Debug, Deserialize, Serialize)]\npub struct Route {\n pub source: String,\n pub destination: String,\n}\n\n#[derive(Debug, Deserialize, Serialize)]\npub struct Router {\n #[serde(rename = \"route\")]\n pub routes: Vec,\n}The Router structure is a Rust representation of the TOML configuration above.pub fn redirect(self, req: Request) -> Result {\n // read the request path from the `spin-path-info` header\n let path_info = req\n .headers()\n .get(\"spin-path-info\")\n .expect(\"cannot get path info from request headers\");\n // if the path is not present in the router configuration,\n // return 404 Not Found.\n let route = match self.path(path_info.to_str()?) {\n Some(r) => r,\n None => return not_found(),\n };\n // otherwise, return the redirect to the destination\n let res = http::Response::builder()\n .status(http::StatusCode::PERMANENT_REDIRECT)\n .header(http::header::LOCATION, route.destination)\n .body(None)?;\n Ok(res)\n}The redirect function is straightforward — it reads the request path from the\nspin-path-info header (make sure to read the undefined\nfor an overview of the HTTP headers present in Spin components), selects the\ncorresponding destination from the router configuration, then sends the\nHTTP redirect to the new location.At this point, we can build and run the module with Spin:$ spin build\n$ spin upAnd the component can now handle incoming requests:# based on the configuration file, a request\n# to /spin should be redirected\n$ curl -i localhost:3000/spin\nHTTP/1.1 308 Permanent Redirect\nlocation: https://github.com/fermyon/spin\ncontent-length: 0\n# based on the configuration file, a request\n# to /hype should be redirected\n$ curl -i localhost:3000/hype\nHTTP/1.1 308 Permanent Redirect\nlocation: https://www.fermyon.com/blog/how-to-think-about-wasm\ncontent-length: 0\n# /abc is not present in the router configuration,\n# so this returns a 404.\n$ curl -i localhost:3000/abc\nHTTP/1.1 404 Not Found\ncontent-length: 9\n\nNot FoundundefinedWe can now undefined (together\nwith router configuration file):$ spin registry push ghcr.io/alyssa-p-hacker/url-shortener:v1\nPushed with digest sha256:3c408a1b29b3098286c7ea5ab22f47248ccfadcc63ad5596ca0d85e3f522c43dAnd now we can run the application directly from the registry:$ spin up -f ghcr.io/alyssa-p-hacker/url-shortener:v1\n\nServing http://127.0.0.1:3000\nAvailable Routes:\n shortener: http://127.0.0.1:3000 (wildcard)","keywords":"","subsectionKeywords":"","url":"/spin/url-shortener.md#here-is-one-we-prepared-earlier"},{"project":"spin","title":"Building a URL shortener with Spin","subheading":"Conclusion","content":"In this tutorial we built a simple URL shortener as a Spin component.\nIn the future we will expand this tutorial by storing the router configuration\nin a database supported by Spin, and potentially create another component that\ncan be used to add new routes to the configuration.","keywords":"","subsectionKeywords":"","url":"/spin/url-shortener.md#conclusion"},{"project":"spin","title":"Writing Spin Applications","subheading":"","content":"undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedA Spin application consists of a set of WebAssembly (Wasm) undefined, and a undefined that lists those components with some data about when and how to run them. This page describes how to write components, manifests, and hence applications.","subsectionKeywords":"","url":"/spin/writing-apps"},{"project":"spin","title":"Writing Spin Applications","subheading":"Writing an Application Manifest","content":"undefinedA Spin undefined is a undefined file that contains:undefinedundefinedundefinedundefinedBy convention, the Spin manifest file is usually called spin.toml.This example is the manifest for a simple HTTP application with a single component executed when the /hello endpoint is accessed:# General identification information\nspin_manifest_version = \"1\"\nname = \"spin-hello-world\"\nversion = \"1.0.0\"\ndescription = \"A simple application that returns hello world.\"\n# The application trigger. This application responds to HTTP requests\ntrigger = { type = \"http\", base = \"/\" }\n\n# The single component\n[[component]]\nid = \"hello\"\ndescription = \"A simple component that returns hello world.\"\n# The Wasm module to run for the component\nsource = \"target/wasm32-wasi/release/helloworld.wasm\"\n# The component trigger. This component responds to HTTP requests to \"/hello\"\n[component.trigger]\nroute = \"/hello\"\n# How to build the Wasm module from source\n[component.build]\ncommand = \"cargo build --target wasm32-wasi --release\"You can look up the various fields in the undefined, but let's look at the key fields in more detail.","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#writing-an-application-manifest"},{"project":"spin","title":"Writing Spin Applications","subheading":"The Application trigger","content":"The most important field in the application-level section is the trigger field. This tells Spin the undefined of event that it should hook the application up to. Spin has built-in support for responding to HTTP requests (trigger = { type = \"http\" }) and Redis messages (trigger = { type = \"redis\" })undefinedSome triggers have additional application-level configuration options. For example, the HTTP trigger requires you to provide a base field, which tells Spin the \"root\" route to which all component routes are relative. See the undefined and undefined documentation for more details.","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#the-application-trigger"},{"project":"spin","title":"Writing Spin Applications","subheading":"The Component id","content":"Component ids are identifier strings. While not significant in themselves, they must be unique within the application. Spin uses the id in logging and error messages to tell you which component a message applies to.[[component]]\nid = \"hello\"","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#the-component-id"},{"project":"spin","title":"Writing Spin Applications","subheading":"The Component source","content":"Each component has a source field which tells Spin where to load the Wasm module from.For components that you are working on locally, set it to the file path to the compiled Wasm module file. This must be a relative path from the directory containing spin.toml.[[component]]\nsource = \"dist/cart.wasm\"For components that are published on the Web, provide a url field containing the URL of the Wasm module, and a digest field indicating the expected SHA256 hash of the module contents. The digest must be prefixed with the string sha256:. Spin uses the digest to check that the module is what you were expecting, and won't load the module if the content doesn't match.[[component]]\nsource = { url = \"https://github.com/fermyon/spin-fileserver/releases/download/v0.0.1/spin_static_fs.wasm\", digest = \"sha256:650376c33a0756b1a52cad7ca670f1126391b79050df0321407da9c741d32375\" }Multiple components can have the same source. An example is a document archive, where one component might serve user interface assets (CSS, images, etc.) on one route, while another serves the documents themselves on another route - both using the same file server module, but with different settings.","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#the-component-source"},{"project":"spin","title":"Writing Spin Applications","subheading":"The Component trigger","content":"Each component has a trigger field which contains per-component settings (as opposed to the application-level ones on the application's trigger). In particular, this tells Spin when to run undefined component rather than any other component. For HTTP applications, the component trigger has a route that says which HTTP route the component should handle. For Redis applications, the component trigger has a channel that says which Redis channel's messages the component should handle. See the undefined and undefined documentation for more details.[[component]]\n[component.trigger]\nroute = \"/hello\"","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#the-component-trigger"},{"project":"spin","title":"Writing Spin Applications","subheading":"Writing a Component Wasm Module","content":"undefinedAt the Wasm level, a Spin component is a Wasm module that exports a handler for the application trigger. At the developer level, a Spin component is a library or program that implements your event handling logic, and uses Spin interfaces, libraries, or tools to associate that with the events handled by Spin.See the Language Guides section for how to do this in your preferred language. As an example, this is a component written in the Rust language. The hello_world function uses an attribute #[http_component] to identify the function as handling a Spin HTTP event. The function takes a Request and returns a Result:#[http_component]​\nfn hello_world(_req: Request) -> Result {​\n Ok(http::Response::builder()​\n .status(200)​\n .body(Some(\"Hello, Fermyon!\".into()))?)​\n}​","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#writing-a-component-wasm-module"},{"project":"spin","title":"Writing Spin Applications","subheading":"Creating an Application From a Template","content":"If you've installed the Spin templates for your preferred language, you can use them to get started without having to write a manifest or the boilerplate code yourself. To do this, run the spin new command, and choose the template that matches the type of application you want to create, and the language you want to use.{{ tabs \"sdk-type\" }}{{ startTab \"Rust\"}}Choose the http-rust template to create a new HTTP application, or redis-rust to create a new Redis application.$ spin new\nPick a template to start your application with:\n http-c (HTTP request handler using C and the Zig toolchain)\n http-csharp (HTTP request handler using C# (EXPERIMENTAL))\n http-go (HTTP request handler using (Tiny)Go)\n http-grain (HTTP request handler using Grain)\n> http-rust (HTTP request handler using Rust)\n http-swift (HTTP request handler using SwiftWasm)\n http-zig (HTTP request handler using Zig)\n redis-go (Redis message handler using (Tiny)Go)\n redis-rust (Redis message handler using Rust)\n\nEnter a name for your new application: hello_rust\nProject description: My first Rust Spin application\nHTTP base: /\nHTTP path: /...{{ blockEnd }}{{ startTab \"TypeScript\"}}Choose the http-ts or http-js template to create a new HTTP application, according to whether you want to use TypeScript or JavaScript.undefined$ spin new\nPick a template to start your application with:\n http-js (HTTP request handler using Javascript)\n> http-ts (HTTP request handler using Typescript)\nEnter a name for your new application: hello_typescript\nProject description: My first TypeScript Spin application\nHTTP base: /\nHTTP path: /...{{ blockEnd }}{{ startTab \"Python\"}}Choose the http-py template to create a new HTTP application.undefined$ spin new\nPick a template to start your application with:\n> http-py (HTTP request handler using Python)\nEnter a name for your new application: hello_python\nDescription: My first Python Spin application\nHTTP base: /\nHTTP path: /...{{ blockEnd }}{{ startTab \"TinyGo\"}}Choose the http-go template to create a HTTP application, or redis-go to create a Redis application.$ spin new\nPick a template to start your application with:\n http-c (HTTP request handler using C and the Zig toolchain)\n http-empty (HTTP application with no components)\n> http-go (HTTP request handler using (Tiny)Go)\n http-grain (HTTP request handler using Grain)\n http-php (HTTP request handler using PHP)\n http-rust (HTTP request handler using Rust)\nEnter a name for your new application: hello_go\nDescription: My first Go Spin application\nHTTP base: /\nHTTP path: /...{{ blockEnd }}{{ blockEnd }}All of these templates create a manifest containing a single component, and the source code for a minimal \"hello world\" component.","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#creating-an-application-from-a-template"},{"project":"spin","title":"Writing Spin Applications","subheading":"Adding a New Component to an Application","content":"To add a new component to an existing application using a template, run the spin add command. This works in a very similar way to spin new, except that it expects the spin.toml file to already exist, and adds the details for the new component to that spin.toml.","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#adding-a-new-component-to-an-application"},{"project":"spin","title":"Writing Spin Applications","subheading":"Including Files with Components","content":"You can include files with a component. This means that:undefinedundefinedTo do this, use the files field in the component manifest:[[component]]\nfiles = [ \"images/**/*.jpg\", { source = \"styles/dist\", destination = \"/styles\" } ]The files field is an array listing the files, patterns and directories you want to include. Each element of the array can be:undefinedundefinedIf your files list would match some files or directories that you undefined want included, you can use the exclude_files field to omit them.undefined","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#including-files-with-components"},{"project":"spin","title":"Writing Spin Applications","subheading":"Adding Environment Variables to Components","content":"Environment variables can be provided to components via the Spin application manifest.To do this, use the environment field in the component manifest:[[component]]\nenvironment = { PET = \"CAT\", FOOD = \"WATERMELON\" }The field accepts a map of environment variable key/value pairs. They are mapped inside the component at runtime.The environment variables can then be accessed inside the component. For example, in Rust:#[http_component]\nfn handle_hello_rust(req: Request) -> Result {\n let response = format!(\"My {} likes to eat {}\", std::env::var(\"PET\")?, std::env::var(\"FOOD\")?);\n Ok(http::Response::builder()\n .status(200)\n .header(\"foo\", \"bar\")\n .body(Some(response.into()))?)\n}","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#adding-environment-variables-to-components"},{"project":"spin","title":"Writing Spin Applications","subheading":"Granting Networking Permissions to Components","content":"By default, Spin components are not allowed to make outgoing HTTP requests. This follows the general Wasm rule that modules must be explicitly granted capabilities, which is important to sandboxing. To grant a component permission to make HTTP requests to a particular host, use the allowed_http_hosts field in the component manifest:[[component]]\nallowed_http_hosts = [ \"dog-facts.example.com\", \"api.example.com:8080\" ]The Wasm module can make HTTP requests undefined to the specified hosts. If a port is specified, the module can make requests only to that port; otherwise, the module can make requests only on the default HTTP and HTTPS ports. Requests to other hosts (or other ports) will fail with an error.For development-time convenience, you can also pass the string \"insecure:allow-all\" in the allowed_http_hosts collection. This allows the Wasm module to make HTTP requests to undefined host and on any port. However, once you've determined which hosts your code needs, you should remove this string, and list the hosts instead. Other Spin implementations may restrict host access, and may disallow components that ask to connect to anything and everything!","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#granting-networking-permissions-to-components"},{"project":"spin","title":"Writing Spin Applications","subheading":"Granting Storage Permissions to Components","content":"By default, Spin components are not allowed to access Spin's storage services. This follows the general Wasm rule that modules must be explicitly granted capabilities, which is important to sandboxing. To grant a component permission to use a Spin-provided store, use the key_value_stores field in the component manifest:[[component]]\nkey_value_stores = [ \"default\" ]See undefined for more information.","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#granting-storage-permissions-to-components"},{"project":"spin","title":"Writing Spin Applications","subheading":"Example Manifests","content":"This section shows some examples of Spin component manifests.","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#example-manifests"},{"project":"spin","title":"Writing Spin Applications","subheading":"Including a Directory of Files","content":"This example shows a Spin HTTP component that includes all the files in static/ under the application directory, made available to the Wasm module at the / path.[[component]]\nsource = \"modules/spin_static_fs.wasm\"\nid = \"fileserver\"\nfiles = [ { source = \"static/\", destination = \"/\" } ]\n[component.trigger]\nroute = \"/static/...\"","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#including-a-directory-of-files"},{"project":"spin","title":"Writing Spin Applications","subheading":"Using Public Components","content":"This is similar to the file server component above, but gets the Wasm module from a public release instead of a local copy. Notice that the files are still drawn from a local path. This is a way in which you can use off-the-shelf component logic with your own application data.[[component]]\nsource = { url = \"https://github.com/fermyon/spin-fileserver/releases/download/v0.0.1/spin_static_fs.wasm\", digest = \"sha256:650376c33a0756b1a52cad7ca670f1126391b79050df0321407da9c741d32375\" }\nid = \"fileserver\"\nfiles = [ { source = \"static/\", destination = \"/\" } ]\n[component.trigger]\nroute = \"/static/...\"","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#using-public-components"},{"project":"spin","title":"Writing Spin Applications","subheading":"Customizing the Executor","content":"This example shows an HTTP component whose Wasm module, instead of using the default Spin HTTP interface, uses the CGI-like WAGI (WebAssembly Gateway Interface) protocol. Spin can't work out the application model from the component, so the manifest needs to tell Spin to use WAGI instead of its default mode. It does this via the executor field. This is specific to the HTTP trigger so it goes under component.trigger.In addition, this module does not provide its WAGI functionality via the default _start entry point, but via a custom entry point named serve-wagi. The executor table needs to tell Spin this via the entrypoint field. Finally, this component needs to run the WAGI entry point with a specific set of command line arguments, which are expressed in the WAGI executor's argv field.[[component]]\nsource = \"modules/env_wagi.wasm\"\nid = \"env\"\nfiles = [ \"content/**/*\" , \"templates/*\", \"scripts/*\", \"config/*\"]\n[component.trigger]\nroute = \"/...\"\nexecutor = { type = \"wagi\", argv = \"test ${SCRIPT_NAME} ${ARGS} done\", entrypoint = \"serve-wagi\" }","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#customizing-the-executor"},{"project":"spin","title":"Writing Spin Applications","subheading":"Setting the Redis Channel to Monitor","content":"The example shows a Redis component. The manifest sets the channel field to say which channel the component should monitor. In this case, the component is invoked for new messages on the messages channel.[[component]]\nid = \"echo-message\"\nsource = \"spinredis.wasm\"\n[component.trigger]\nchannel = \"messages\"","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#setting-the-redis-channel-to-monitor"},{"project":"spin","title":"Writing Spin Applications","subheading":"Next Steps","content":"undefinedundefinedundefinedundefinedundefinedundefined","keywords":"","subsectionKeywords":"","url":"/spin/writing-apps.md#next-steps"}] \ No newline at end of file