First draft Sandstorm API.
This commit is contained in:
commit
80b273b75e
3 files changed
+701
No files matched your search
@@ -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.
|
||||
}
|
||||
}
|
||||
@@ -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) -> ();
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user