mirror of
https://github.com/sandstorm-io/sandstorm.git
synced 2026-11-07 10:02:45 -05:00
214 lines
9.2 KiB
Markdown
214 lines
9.2 KiB
Markdown
A Sandstorm app can publish **static web content** to **any domain**
|
|
of the user's choosing. This is useful for content management systems,
|
|
blogging platforms, and other apps. This guide helps app authors use
|
|
that feature of the Sandstorm platform.
|
|
|
|
## Overview
|
|
|
|
In Sandstorm, an app can publish **static HTML** and other files in a
|
|
way where Sandstorm will serve the content, rather than the app. This
|
|
saves on compute time and limits attack surface.
|
|
|
|
You can **[try it
|
|
now](https://oasis.sandstorm.io/appdemo/qn94a65er7m7s3zgwrsnv8hhh81dw5mc8zpt75a8su30dqhv9gt0)**
|
|
with this sample app ([full source
|
|
available](https://github.com/paulproteus/sandstorm-sample-static-publishing/)).
|
|
|
|
**Store static files in `/var/www`**. Each directory within `/var/www`
|
|
should contain a file called `index.html`.
|
|
|
|
The grain can ask Sandstorm to enable publishing by requesting the
|
|
creation of a unique `publicId`. Once that's done, the files are
|
|
**available on a special subdomain** of the Sandstorm install. The
|
|
subdomain takes the form `publicId.sandstorm.example.com`.
|
|
|
|
Users can also **make the content available at any domain**. To do
|
|
that, they need to configure **a CNAME record** pointing at the
|
|
Sandstorm install; this is how their domain's DNS will resolve to the
|
|
Sandstorm server. The user also needs **a TXT record**; this is how
|
|
Sandstorm determines what `publicId` this domain corresponds to.
|
|
|
|
This page explains how to generate a `publicId` and how to instruct a
|
|
user of your app to configure their DNS appropriately. This a
|
|
**provisional API**; see the note below about how we aim to make this
|
|
more usable and more secure.
|
|
|
|
## A helper program you can include, to enable publishing & request a publicId
|
|
|
|
The simplest way to enable static publishing is to embed a small C++
|
|
program with your app. The [Sample Static Publishing
|
|
App](https://github.com/paulproteus/sandstorm-sample-static-publishing/)
|
|
includes that C++ code.
|
|
|
|
To include the C++ code in your app, copy [these
|
|
files](https://github.com/paulproteus/sandstorm-sample-static-publishing/tree/master/sandstorm-integration)
|
|
into a directory of your app called `sandstorm-integration`.
|
|
|
|
If you are using `vagrant-spk`, also add the following to the end of your
|
|
`.sandstorm/setup.sh`:
|
|
|
|
```bash
|
|
### Download & compile capnproto and the Sandstorm getPublicId helper.
|
|
|
|
# First, get capnproto from master and install it to
|
|
# /usr/local/bin. This requires a C++ compiler. We opt for clang
|
|
# because that's what Sandstorm is typically compiled with.
|
|
if [ ! -e /usr/local/bin/capnp ] ; then
|
|
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -q clang autoconf pkg-config libtool git
|
|
cd /tmp
|
|
if [ ! -e capnproto ]; then git clone https://github.com/sandstorm-io/capnproto; fi
|
|
cd capnproto
|
|
git checkout master
|
|
cd c++
|
|
autoreconf -i
|
|
./configure
|
|
make -j2
|
|
sudo make install
|
|
fi
|
|
|
|
# Second, compile the small C++ program within
|
|
# /opt/app/sandstorm-integration.
|
|
if [ ! -e /opt/app/sandstorm-integration/getPublicId ] ; then
|
|
pushd /opt/app/sandstorm-integration
|
|
make
|
|
fi
|
|
### All done.
|
|
```
|
|
|
|
Now, run the improved `.sandstorm/setup.sh` script by doing:
|
|
|
|
```bash
|
|
vagrant-spk vm provision
|
|
```
|
|
|
|
This should result in a
|
|
`/opt/app/sandstorm-integration/bin/getPublicId` binary showing up in
|
|
your directory tree. When you run it and provide the current
|
|
`X-Sandstorm-Session-Id` header as a command-line parameter, it will
|
|
output a `publicId` (and other information, see below).
|
|
|
|
If you prefer to use your own build system, you can use the above as
|
|
inspiration. If you prefer to call Sandstorm's RPC directly, keep
|
|
reading.
|
|
|
|
**Note**: At the time of writing, some vagrant-spk stacks crash if you
|
|
run `vagrant-spk vm provision` a second time. We're [working on fixing
|
|
that.](https://github.com/sandstorm-io/vagrant-spk/issues/87)
|
|
|
|
## Show DNS instructions to the user
|
|
|
|
You should make sure the user knows how to configure their domain's
|
|
CNAME record (to point at the Sandstorm install) and TXT record (to
|
|
tell Sandstorm which grain the domain points at).
|
|
|
|
You can find sample text in the [Sandstorm sample app with static
|
|
publishing](https://github.com/paulproteus/sandstorm-sample-static-publishing/blob/master/after_publish.php). The essentials are:
|
|
|
|
* The user can preview their site at the `autoUrl` link. The `bin/getPublicId` program
|
|
prints that as line #3 (`lines[2]` in 0-indexed programming languages).
|
|
|
|
* The user should set their CNAME value to the host component of the
|
|
`autoUrl`, for example `publicId.sandstorm.example.com`. You can
|
|
calculate this host component of the URL by parsing the `autoUrl`.
|
|
|
|
* The user should set up a TXT record at `sandstorm-www.example.com`
|
|
containing just the `publicId`. The `bin/getPublicId` program prints
|
|
that as line 1 (`lines[0]` in 0-indexed programming languages).
|
|
|
|
## Using the Sandstorm Cap'n Proto APIs directly
|
|
|
|
You can access the Sandstorm `HackSessionContext` capability directly
|
|
if you want more performance or prefer to use the Sandstorm APIs with
|
|
no overhead.
|
|
|
|
**Start by obtaining a `HackSessionContext` capability.**
|
|
[`HackSessionContext`](https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/hack-session.capnp)
|
|
is a [Cap'n Proto](https://capnproto.org) interface. You must obtain
|
|
an instance of this capability. The way to do this depends on whether
|
|
your app uses `sandstorm-http-bridge` (check your
|
|
`sandstorm-pkgdef.capnp` to find out).
|
|
|
|
If you are using the raw Cap'n Proto API without the HTTP Bridge, then
|
|
the `SessionContext` capability you receive as a parameter to the
|
|
`UiView.newSession()` method can be cast to `HackSessionContext`. *
|
|
If you are using `sandstorm-http-bridge`, you must open a Cap'n Proto
|
|
connection to `unix:/tmp/sandstorm-api`, which will give you a
|
|
[SandstormHttpBridge](https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/sandstorm-http-bridge.capnp)
|
|
capability. Call `getSessionContext()` on that capability, using the
|
|
ID that sandstorm-http-bridge places in the `X-Sandstorm-Session-Id`
|
|
header, and cast the result to a `HackSessionContext`.
|
|
|
|
Note that `HackSessionContext` is a temporary API. As described below,
|
|
we intend to replace this with a better API later.
|
|
|
|
**Then call `getPublicId()` on the context.** The first time you call
|
|
this, the grain (app instance) is assigned a "public ID", which is a
|
|
random string that uniquely identifies the grain, but which differs
|
|
from the "private ID" which appears in the grain's URL. The public ID
|
|
is not a secret as it grants no authority over the grain, whereas
|
|
anyone who knows the private ID has full control over the grain.
|
|
|
|
The method call returns the grain's public ID as well as the hostname
|
|
at which the server is hosted.
|
|
|
|
Then, instruct the user on how to set up DNS.
|
|
|
|
The user will need to set two DNS records:
|
|
|
|
<user-host> IN CNAME `<autoUrl>`
|
|
sandstorm-www.<user-host> IN TXT <public-id>
|
|
|
|
where:
|
|
|
|
* `<user-host>` is the hostname at which the user wishes to publish their site.
|
|
* `<autoUrl>` is the `<public-id>` plus the Sandstorm server hostname (as returned by `getPublicId()`).
|
|
* `<server-host>` is the hostname of the Sandstorm server (as returned by `getPublicId()`).
|
|
|
|
## Raw API example
|
|
|
|
See the Hacker CMS app, which can be installed from the [app
|
|
list](https://sandstorm.io/apps) or from [source
|
|
code](https://github.com/kentonv/ssjekyll).
|
|
|
|
## Note: Provisional API
|
|
|
|
The current Cap'n Proto RPC for web publishing is hacky and not
|
|
intended to be the long-term solution. In the long term, users will be
|
|
able to connect domains to their Sandstorm account and then grant them
|
|
to apps as capabilities through the Powerbox UI. Since the Powerbox
|
|
and persistent capabilities are not yet implemented -- much less the
|
|
ability to connect domains -- we are providing a hack so that
|
|
developers can get started on such apps now. The hack allows a user to
|
|
designate a Sandstorm app to host their domain via a special TXT
|
|
record.
|
|
|
|
Also, the use of a C++ binary that you must embed might not be the
|
|
most convenient way to expose the Cap'n Proto API. We're considering
|
|
creating a pure-frontend Javascript API like [offer
|
|
templates](http-apis.md) or a `postMessage`-based API, and/or a
|
|
backend API that is part of `sandstorm-http-bridge`. Let us know if
|
|
you have a preference for what you would like to use.
|
|
|
|
## Why only static content?
|
|
|
|
In order to make per-user application instances cost-effective, a
|
|
Sandstorm application server normally only runs while a user has the
|
|
application open. This works very well for things like private
|
|
documents which have only one or maybe a few users. A public web site,
|
|
however, is intended to be viewed widely and at all hours of the
|
|
day. If we had to spin up the application sever for every visit, we'd
|
|
lose this key advantage.
|
|
|
|
By restricting web publishing to static content, we can avoid spinning
|
|
up the application server for regular visits. Only editing the content
|
|
requires the server to be active.
|
|
|
|
As of this writing, there is no way to publish dynamic web sites to a
|
|
custom domain via Sandstorm. In the future, this will become possible
|
|
via the use of APIs. Once an application can export a public API, then
|
|
it will be possible for "static" javascript published on a domain to
|
|
make calls to that API. Such calls will, of course, require spinning
|
|
up the server to handle, but a well-written app may be able to avoid
|
|
making API calls except under special circumstances (e.g. when a user
|
|
clicks to post a comment).
|