First draft Sandstorm API.

This commit is contained in:
Kenton Varda committed 2014-01-07 23:03:07 -08:00
commit 80b273b75e
3 files changed
+701

No files matched your search

+496
View File
@@ -0,0 +1,496 @@
# Sandstorm - Personal Cloud Sandbox
# Copyright (c) 2014, Kenton Varda <temporal@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@0xc8d91463cfc4fb4a;
$import "/capnp/c++.capnp".namespace("sandstorm");
using Util = import "util.capnp";
# ========================================================================================
# Manifest file
struct Manifest {
# This manifest file defines an application. The file `sandstorm-manifest` at the root of the
# application's `.spk` package contains a serialized (binary) instance of `Manifest`.
minApiVersion @0 :UInt32;
maxApiVersion @1 :UInt32;
# Min and max API versions against which this app is known to work. `minApiVersion` primarily
# exists to warn the user if their instance is too old. If the sandstorm instance is newer than
# `maxApiVersion`, it may engage backwards-compatibility hacks and hide features introduced in
# newer versions.
struct Command {
# Description of a command to execute.
#
# Note that commands specified this way are NOT interpreted by a shell. If you want shell
# expansion, you must include a shell binary in your app and invoke it to interpret the
# command.
executablePath @0 :Text; # Executable filename, within the .spk package.
args @1 :List(Text); # argv list; first argument should be program name.
environ @2 :List(Util.KeyValue);
# Environment variables to set. The environment will be completely empty other than what you
# define here.
}
struct Action {
input :union {
none @0 :Void;
# This action creates a new grain with no input. On startup, the grain will export a `UiView`
# as its default capability.
capability @1 :PowerboxQuery;
# This action creates a new grain from a capability. When a capability matching the query
# is offered to the user (e.g. by another application calling SessionContext.offer()), this
# action will be listed as one of the things the user can do with it.
#
# On startup, the grain will export a `PowerboxAction` as its default capability.
}
command @2 :Command;
# Command to execute (in a newly-allocated grain) to run this action.
title @3 :Util.LocalizedText;
# Title of this action, to display in the action selector.
description @4 :Util.LocalizedText;
# Description of this action, suitable for help text.
}
actions @2 :List(Action);
# Actions which this grain offers.
continueCommand @3 :Command;
# Command to run to restart an already-created grain.
}
# ========================================================================================
# Runtime interface
interface Supervisor {
# The Sandstorm platform API, exposed as the default capability over the two-way RPC connection
# formed with the application instance. This object specifically represents the supervisor
# for this application instance -- two different application instances (grains) never share a
# supervisor.
# TODO(soon): Read the grain title as set by the user. Also have interface to offer a new
# title and icon?
publish @0 (cap :PowerboxCapability, requiredPermissions :PermissionSet)
-> (registration :Util.Handle);
# Publish the given capability, such that any user who has access to the Grain with at least the
# given permissions will see the capability show up in their powerbox for matching searches.
#
# Returns a handle which, when dropped, un-publishes the capability. You must save this handle
# to storage to keep the capability published. Dropping the handle does not revoke the
# capability, only prevents it from appearing in the powerbox going forward.
# TODO(someday): Let the app publish a picker UI which can be embedded in the powerbox. For
# example, a music library may want to make each track in the library available via the
# powerbox, but publishing every one may be clunky. Instead, it might offer a little embedded
# UI which lets the user type a search query or browse by artist.
registerAction @1 (query :PowerboxQuery, action :PowerboxAction,
requiredPermissions :PermissionSet) -> (registration :Util.Handle);
# Register an action implemented by this grain. It will be offered to the user as an option
# whenever another app uses `SessionContext.offer()` to offer a capability that matches the query.
#
# This method should only be used for actions that are somehow specific to this grain. This is
# unusual. Usually, you want to define actions in the application's manifest; such actions will
# be executed in a newly-created grain.
shareCap @2 (cap :PowerboxCapability) -> (sharedCap :PowerboxCapability, link :SharingLink);
# Share a capability, so that it may safely be sent to another user. `sharedCap` is a wrapper
# (membrane) around `cap` which can have a petname assigned and can be revoked via `link`. The
# share is automatically revoked if `link` is discarded. If `cap` is persistable, then both
# `sharedCap` and `link` also are.
#
# This method is intended to be used by programs that acutally implement a communications link
# over which a capability could be sent from one user to another. For example, a chat app would
# use this to prepare a capability to be embedded into a message. In these cases, capabilities
# may be shared without going through the system sharing UI, and therefore the application must
# set up the sharing link itself.
#
# In general, you should NOT call this on a capability that you will then pass to
# `SessionContext.offer()`.
shareView @3 (view :UiView) -> (sharedView :UiView, link :ViewSharingLink);
# Like `shareCap` but with extra options for sharing a UiView, such as setting a role and
# permissions.
}
interface UiView extends(PowerboxCapability) {
# Implements a user interface with which a user can interact with the grain. We call this a
# "view" because a single grain may actually have multiple "views" that provide different
# functionality or represent multiple logical objects in the same physical grain.
#
# When an application starts up, it must export an instance of UiView as its starting
# capability on the Cap'n Proto two-party connection. This represents the grain's main view and
# is what the user will see when they open the grain.
#
# It is possible for a grain to export additional views via the usual powerbox mechanisms. For
# instance, a spreadsheet app might let the user create a "view" of a few cells of the
# spreadsheet, allowing them to share those cells to another user without sharing the entire
# sheet. To accomplish this, the app would create an alternate UserInterface object that
# implements an interface just to those cells, and then would use `UiSession.offer()` to offer
# this object to the user. The user could then choose to open it, share it, save it for later,
# etc.
getViewInfo @0 () -> ViewInfo;
# Get metadata about the view, especially relating to sharing.
struct ViewInfo {
permissions @0 :List(PermissionDef);
# List of permission bits which apply to this view. Permissions typically include things like
# "read" and "write". When sharing a view, the sending user may select a set of permissions to
# grant to the receiving user, and may modify this set later on. When a new user interface
# session is initiated, the platform indicates which permissions the user currently has.
#
# The grain's owner always has all permissions.
#
# It is important that new versions of the app only add new permissions, never remove existing
# ones, since permission IDs are indexes into the list and persist through upgrades.
#
# In a true capability system, permissions would normally be implemented by wrapping the main
# view in filters that prohibit disallowed actions. For example, to give a user read-only
# access to a grain, you might wrap its UiView in a wrapper that checks all incoming requests
# and disallows the ones that would modify the content. However, this approach does not work
# terribly well for UiView for a few reasons:
#
# - For complex UIs, HTTP is often the wrong level of abstraction for this kind of filtering.
# It _may_ work for modern apps that push all UI logic into static client-side Javascript and
# only serve RPCs over dynamic HTTP, but it won't work well for many legacy apps, and we want
# to be able to port some of those apps to Sandstorm.
#
# - If a UiView is reshared several times, each time adding a new filtering wrapper, then
# requests could get slow as they have to pass through all the separate filters. This would
# be especially bad if some of the filters live in other grains, as those grains would have
# to spin up whenever the resulting view is used.
#
# - Compared to computers, humans are relatively less likely to be vulnerable to confused
# deputy attacks and relatively more likely to be confused by the concept of having multiple
# capabilities to the same object that provide different access levels. For example, say
# Alice and Bob both share the same document to Carol, but Alice only grants read access
# while Bob gives read/write. Carol should only see one instance of the document in her
# grain list and she should see the read/write interface when she opens it. But this instance
# isn't simply the one she got from Bob -- if Bob revokes his share but Alice continues to
# share read rights, Carol should now see the read-only interface when she opens the same
# grain.
#
# To solve all three problems, we have permission bits that are processed when creating a new
# session. Instead of filtering individual requests, wrappers of UiView only need to filter
# calls to `newSession()` in order to restrict the permission set as appropriate. Once a
# session is thus created, it represents a direct link to the target grain. Also, the platform
# can implement special handling of sharing and permission bits that allow it to recognize when
# two UiViews are really the same view with different permissions applied, and can then combine
# them in the UI as appropriate.
#
# It is actually entirely possible to implement a traditional filtering membrane around a
# UiView, perhaps to implement a kind of access that can't be expressed using the permission
# bits defined by the app. But doing so will be awkward, slow, and confusing for all the
# reasons listed above.
roles @1 :List(RoleDef);
# Choosing individual permissions is not very intuitive for most users. Therefore, the sharing
# interface prefers to offer the user a list of "roles" to assign to each recipient. For
# example, a document might have roles like "editor" and "viewer". Each role corresponds to
# some list of permissions. The application may define a set of roles to offer via this list.
#
# In addition to the roles in this list, the sharing interface will always offer a "full access"
# or "same as me" option. So, it only makes sense to define roles that represent less than
# "full access", and leaving the role list entirely empty is reasonable if there are no such
# restrictions to offer.
#
# It is important that new versions of the app only add new roles, never remove existing ones,
# since role IDs are indexes into the list and persist through upgrades.
deniedPermissions @2 :PermissionSet;
# Set of permissions which will be removed from the permission set when creating a new session
# though this object. This set should be empty for the grain's main UiView, but when that view
# is shared with less than full access, recipients will get a proxy UiView which has a non-empty
# `deniedPermissions` set.
#
# It is not the caller's responsibility to enforce this set. It is provided mainly so that the
# sharing UI can avoid offering options to the user that don't make sense. For instance, if
# Alice has read-only access to a document and wishes to share the document to Bob, the sharing
# UI should not offer Alice the ability to share write access, because she doesn't have it in
# the first place. The sharing UI figures out what Alice has by examining `deniedPermissions`.
}
newSession @1 (userInfo :UserInfo, context :SessionContext,
sessionType :UInt64, sessionParams :AnyPointer)
-> (session :UiSession);
# Start a new user interface session. This happens when a user first opens the view, or when
# the user returns to a tab that has been inactive long enough that the server was killed off in
# the meantime.
#
# `userInfo` specifies the user's display name and permissions, as authenticated by the system.
#
# `context` contains callbacks that can be used to invoke system functionlity in the context of
# the session, such as displaying the powerbox.
#
# `sessionType` is the type ID specifying the interface which the returned `session` should
# implement. All views should support the `WebSession` interface to support opening the view
# in a browser. Other session types might be useful for e.g. desktop and mobile apps.
#
# `sessionParams` is a struct whose type is specified by the session type. By convention, this
# struct should be defined nested in the session interface type with name "Params", e.g.
# `WebSession.Params`. This sturct contains some arbitrary startup information.
}
# ========================================================================================
# User interface sessions
interface UiSession {
# Base interface for UI sessions. The most common subclass is `WebSession`.
}
struct UserInfo {
# Information about the user opening a new session.
#
# TODO(soon): More details:
# - Profile: Name, avatar, profile link
# - Sharing/authority chain: "Carol (via Bob, via Alice)"
# - Identity: Public key, certificates, verification of proxy chain.
displayName @0 :Util.LocalizedText;
# Name by which to identify this user within the user interface. For example, if two users are
# editing a document simultaneously, the application may display each user's cursor position to
# the other, labeled with the respective display names. As the users edit the document, the
# document's history may be annotated with the display name of the user who made each change.
# Display names are NOT unique nor stable: two users could potentially have the same display
# name and a user's display name could change.
permissions @1 :PermissionSet;
# Set of permissions which this user has. The exact set might not correspond directly to any
# particular role for a number of reasons:
# - The sharer may have toggled individual permissions through the advanced settings.
# - If two different users share different roles to a third user, and neither of the roles is a
# strict superset of the other, the user gets the union of the two permissions.
# - If Alice shares role A to Bob, and Bob further delegates role B to Carol, then Carol's
# permissions are the intersection of those granted by roles A and B.
#
# That said, some combinations of permissions may not make sense. For example, a document editor
# probably has no reasonable way to implement write permission without read permission. It is up
# to the application to decide what to do in this case, but simply ignoring the nonsensical
# permissions is often a fine strategy.
#
# If the user's permissions are reduced while the session is opened, the session will be closed
# by the platform and the user forced to start a new one. If the user's permissions are increased
# while the session is opened, the system will prompt them to start a new session to use the new
# permissions. Either way, the application need not worry about permissions changing during a
# session.
}
interface SessionContext {
# Interface that the application can use to call back to the platform in the context of a
# particular session. This can be used e.g. to ask the platform to present certain system
# dialogs to the user.
getSharedPermissions @0 () -> (var :Util.Variable);
# Returns a `Variable(PermissionSet)` indicating the permissions held by the user of this session.
# This variable can be persisted beyond the end of the session. This is useful for detecting if
# the user later loses their access and auto-revoking things in that case. See also `tieToUser()`
# for an easier way to make a particular capability auto-revoke if the user's permissions change.
tieToUser @1 (cap :PowerboxCapability, requiredPermissions :PermissionSet)
-> (tiedCap :PowerboxCapability);
# Create a version of `cap` which will automatically stop working if the user no longer holds the
# permissions indicated by `requiredPermissions` (and starts working again if the user regains
# those permissions). The capability also appears connected to the user in the sharing
# visualization.
#
# Keep in mind that, security-wise, calling this also implies exposing `tiedCap` to the user, as
# anyone with a UiView capability can always initiate a session and pass in their own
# `SessionContext`. If you need to auto-revoke a capability based on the user's permissions
# _without_ actually passing that capability to the user, use `getSharedPermissions()` to detect
# when the user's permissions change and implement it yourself.
offer @2 (cap :PowerboxCapability, requiredPermissions :PermissionSet) -> ();
# Offer a capability to the user. A dialog box will ask the user what they want to do with it.
# Depending on the type of capability, different options may be provided. All capabilities will
# offer the user the option to save the capability to their capability/grain store. Other
# type-specific actions may be offered by the platform or by other applications.
#
# For example, offering a UiView will give the user options like "open in new tab", "save to
# grain list", and "share with another user".
#
# The capability is implicitly tied to the user as if via `tieToUser()`.
request @3 (query :PowerboxQuery) -> (cap :PowerboxCapability);
# Searches for capabilities in the user's powerbox matching the given query and displays a
# selection UI to the user. Returns the selected capability.
}
# ========================================================================================
# Sharing and Access Control
struct PermissionDef {
# Metadata describing a permission bit.
title @0 :Util.LocalizedText;
# Name of the permission, e.g. to display in a checklist of permissions that may be assigned when
# sharing.
description @1 :Util.LocalizedText;
# Prose describing what this permission means, suitable for a tool tip or similar help text.
obsolete @2 :Bool = false;
# If true, this permission was relevant in a previous version of the application but should no
# longer be offered to the user in future sharing actions.
}
using PermissionSet = Data;
# Set of permission IDs. Currently represented as a bitfield. The bits of the byte string are
# interpreted in little-endian order, e.g. the least-significant bit of the first byte represents
# permission 0 while the most-significant bit of the third byte represents permission 23. All
# bits beyond the end of the byte string are assumed to be zero.
struct RoleDef {
# Metadata describing a sharable role.
title @0 :Util.LocalizedText;
# Name of the role, e.g. "editor" or "viewer".
verbPhrase @1 :Util.LocalizedText;
# Verb phrase describing what users in this role can do with the grain. Should be something
# like "can edit" or "can view". When the user shares the view with others, these verb phrases
# will be used to populate a drop-list of roles for the user to select.
description @2 :Util.LocalizedText;
# Prose describing what this role means, suitable for a tool tip or similar help text.
permissions @3 :PermissionSet;
# Permissions which make up this role. For example, the "editor" role on a document would
# typically include "read" and "write" permissions.
obsolete @4 :Bool = false;
# If true, this role was relevant in a previous version of the application but should no longer
# be offered to the user in future sharing actions. The role may still be displayed if it was
# used to share the view while still running the old version.
}
interface SharingLink {
# Represents one link in the sharing graph.
getPetname @0 () -> (name :Util.Variable);
# Name assigned by the sharer to the recipient. Variable holds `LocalizedText`.
}
interface ViewSharingLink extends(SharingLink) {
getRoleAssignment @0 () -> (var :Util.Variable);
# Returns a Variable containing a RoleAssignment.
struct RoleAssignment {
union {
allAccess @0 :Void; # Grant all permissions.
roleId @1 :UInt16; # Grant permissions for the given role.
}
addPermissions @2 :PermissionSet;
# Permissions to add on top of those granted above.
removePermissions @3 :PermissionSet;
# Permissions to remove from those granted above.
}
}
# ========================================================================================
# Powerbox
#
# The powerbox is part of the Sandstorm UI which allows users to connect applications to each
# other. First, one application must publish a PowerboxCapability to the powerbox via
# `Supervisor.publish()`. Then, a second application may request a capability from the powerbox
# via `SessionContext.request()`. The user is presented with a list of capabilities from the
# powerbox which match the requesting application's query. The user may select one, which will
# then be returned to the requesting app.
#
# Another way to connect apps is via actions. One application may declare that it can implement
# some action on any capability matching a particular query. Another application may later use
# SessionContext.offer() to offer a specific capability to the current user. The user will be
# presented with a list of actions that make sense for that capability and may select one. (This
# is much like Android intents.)
interface PowerboxCapability {
# Capabilities to be offered to the powerbox must implement PowerboxCapability (in addition to
# the interface for the application functionality they provide). PowerboxCapability provides
# metadata about the capability for display in the powerbox UI as well as the sharing graph
# (which shows inter-app capabilities).
#
# Capabilities sent directly between two grains (e.g. as an RPC parameter) rather than through
# the powerbox UI may also wish to implement PowerboxCapability in order to improve the sharing
# graph visualization. Only persisted capabilities will ever show up in the visualization.
getPowerboxInfo @0 () -> PowerboxInfo;
struct PowerboxInfo {
title @0 :Util.LocalizedText;
# Title for this capability if displayed as an option in the powerbox. Useful for
# distinguishing from other capabilities exported by the same grain. Leave blank if the
# capability effectively represents the entire grain and so should just take the grain's title.
#
# Titles are suggestions only. The user may override the title with their own.
verbPhrase @1 :Util.LocalizedText;
# Verb phrase describing what the holder of this capability can do to the grain, e.g.
# "can edit". This may be displayed in the sharing UI to describe a connection between two
# grains.
description @2 :Util.LocalizedText;
# Long-form description of what the capability represents. Should be roughly a paragraph that
# could be displayed e.g. in a tooltip.
interfaces @3 :List(UInt64);
# Type IDs of Cap'n Proto interfaces implemented by this capability. Only interfaces which
# should be used for powerbox searching/matching purposes need to be listed. Superclasses
# must be listed explicitly if they should be considered for matching. The powerbox does not
# know anything about the actual interface schemas; it just matches the IDs.
# TODO(someday): Icon.
# TODO(someday): Other match criteria.
}
}
interface PowerboxAction {
apply @0 (cap: PowerboxCapability) -> (view :UiView);
# Invoke the action on the given capability, producing a view which is displayed to the user.
}
struct PowerboxQuery {
# When the app requests a capability from the powerbox, it provides a PowerboxQuery specifying
# what kind of capability it wants, in order to narrow down the options.
union {
conjunction @0 :List(PowerboxQuery);
disjunction @1 :List(PowerboxQuery);
# Specifies a conjunction (AND) or disjunction (OR) of other queries.
implements @2 :UInt64;
# The capability must list the given interface in `PowerboxInfo.interfaces`.
# TODO(someday): Other match criteria.
}
}
+90
View File
@@ -0,0 +1,90 @@
# Sandstorm - Personal Cloud Sandbox
# Copyright (c) 2014, Kenton Varda <temporal@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@0xecd50d792c3d9992;
$import "/capnp/c++.capnp".namespace("sandstorm");
struct KeyValue {
key @0 :Text;
value @1 :Text;
}
struct LocalizedText {
# Text intended to be displayed to a user. May be localized to multiple languages.
#
# TODO(soon): Maybe instead of packing all translations in here, we should have a message code
# and parameter substitutions, with the (message code, locale) -> text map stored elsewhere?
defaultText @0 :Text;
# What to display if no localization matching the user's preferences is available.
localizations @1 :List(Localization);
# Localized versions of the text.
struct Localization {
locale @0 :Text; # IETF BCP 47 locale, e.g. "en" or "en-US".
text @1 :Text; # Localized text.
}
}
interface Handle {
# Arbitrary handle to some resource provided by the platform. Can be persisted.
#
# To "drop" a handle means to discard any live references and delete any sturdy references.
# The purpose of a handle is to detect when it has been dropped and to free the underlying
# resource at that time.
}
interface Variable {
# A "variable" -- a value that changes over time. Supports subscribing to updates.
#
# TODO(someday): This should be a parameterized type, when Cap'n Proto supports that.
get @0 () -> (value :AnyPointer, setter :Setter);
# The returned setter's set() can only be called once, and throws an exception if the variable
# has changed since `getForUpdate()` was called. This can be used to implement optimistic
# concurrency.
asGetter @1 () -> (getter :Getter);
# Return a read-only capability for this variable, co-hosted with the variable itself for
# performance. If the varibale is persistable, the getter is as well.
asSetter @2 () -> (setter :Setter);
# Return a write-only capability for this variable, co-hosted with the variable itself for
# performance. If the varibale is persistable, the setter is as well.
interface Getter {
get @0 () -> (value :AnyPointer);
pushTo @1 (setter :Setter) -> (handle :Handle);
# Subscribe to updates. Calls the given setter any time the variable's value changes. Drop
# the returned handle to stop receiving updates. If the variable is persistent, `setter` must
# be as well.
}
interface Setter {
set @0 (value :AnyPointer) -> ();
}
}
+115
View File
@@ -0,0 +1,115 @@
# Sandstorm - Personal Cloud Sandbox
# Copyright (c) 2014, Kenton Varda <temporal@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@0xa8cb0f2f1a756b32;
$import "/capnp/c++.capnp".namespace("sandstorm");
using Grain = import "grain.capnp";
using Util = import "util.capnp";
interface WebSession @0xa50711a14d35a8ce extends(Grain.UiSession) {
# A UI session based on the web platform. The user's browser communicates to the server through
# HTTP requests.
#
# Many of the details of HTTP are implemented by the platform and thus not exposed here. For
# example, the platform may automatically set last-modified based on the last time the
# application's storage was written and may automatically implement etags based on hashing the
# content.
struct Params {
# Startup params for web sessions. See `UiView.newSession()`.
basePath @0 :Text;
# HTTP URL of the application's root directory as seen by this user, e.g.
# "https://ioa5fiu34sm4w.example.com/i7efqesOldepw". Never includes the trailing '/'. Useful
# for constructing intra-app link URLs, although in general you should try to use relative URLs
# whenever possible. Note that the URL can change from session to session and from user to user,
# hence it is only valid for the current session.
userAgent @1 :Text;
acceptableLanguages @2 :List(Text);
acceptableEncodings @3 :List(Text);
# Content of User-Agent, Accept-Language, and Accept-Encoding headers. The platform will start
# a new session if any of these change.
}
get @0 (path :Text, context :Context) -> Response;
post @1 (path :Text, content :PostContent, context :Context) -> Response;
struct Context {
# Additional per-request context.
cookies @0 :List(Util.KeyValue);
}
struct PostContent {
mimeType @0 :Text;
content @1 :Data;
}
struct Response {
setCookies @0 :List(Util.KeyValue);
union {
redirect @1 :Text;
# Redirect to the given URL.
content :group {
# Return content (status code 200).
encoding @2 :Text = "identity"; # Content-Encoding header.
language @3 :Text; # Content-Language header (optional).
mimeType @4 :Text; # Content-Type header.
body :union {
bytes @5 :Data;
stream @6 :Stream;
}
}
error :group {
# Error. The platform will generate a suitable error page.
statusCode @7 :UInt16 = 500;
# HTTP status code. Must be 4xx or 5xx.
# TODO(soon): Some error codes have defined user-agent behavior. Do we need to restrict
# to an enumerated list?
statusText @8 :Text = "Internal Server Error";
# Short error message, e.g. "Not found".
descriptionHtml @9 :Text;
# Optional extended description of the error. If provided, should be an HTML fragment
# containing one or more block-level elements (typically, `<p>`s).
}
# TODO(someday): Return blob directly from storage, so data doesn't have to stream through
# the app?
}
}
interface Stream {
# TODO(someday): Allow streaming responses.
}
}