Documentation
¶
Overview ¶
Package server serves the runner protocol to one app: runner.sock, and mysql.sock through mysqlproxy when the app declares sqldb. It checks everything the app sends against the protocol and the app's permission list: the protocol version, secret names, file names, mail and its recipients, calls to other apps and the size of spans. Only what passes reaches the providers, which keep the files, send the mail, hold the secrets and take the spans on the target the app runs on. A provider gives the app an error of the error catalogue with CodedError, which the app reads as the framework's own coded errors.
aicoded dev, aicoded l7-runner and the platform's runner serve apps through it. Apps never import it; they reach their runner through the building blocks.
Index ¶
Constants ¶
const ( EnvDev = "dev" EnvPreview = "preview" EnvProduction = "production" )
The environments an app may run in.
const MaxFileSize = 1 << 30
MaxFileSize bounds one file in a store, in bytes.
const MinCSRFKey = 32
MinCSRFKey is the shortest key Options.CSRFKey may be, in bytes.
Variables ¶
This section is empty.
Functions ¶
func CodedError ¶
CodedError returns the error a provider gives the app for a problem of the error catalogue: a *connect.Error of c whose message is the code, such as E-MAIL-008, msg, the one-line fix and the code's docs link, as the framework's own coded errors print them.
Types ¶
type Calls ¶
type Calls interface {
// Call checks req, a call made by the app of caller, and forwards it with traceparent.
Call(ctx context.Context, caller manifest.Manifest, req *runnerv1.CallRequest, traceparent string) (*runnerv1.CallResponse, error)
}
Calls carries the calls of an app to other apps.
type FileInfo ¶
type FileInfo struct {
// Name is the last element of the file's name.
Name string
Size int64
Dir bool
ModTime time.Time
}
FileInfo describes a file or a folder in a store.
type Files ¶
type Files interface {
// Stat describes the file or folder name.
Stat(ctx context.Context, store, name string) (FileInfo, error)
// ReadDir lists the folder name, in any order.
ReadDir(ctx context.Context, store, name string) ([]FileInfo, error)
// Read opens the file name, which Stat found to be a file.
Read(ctx context.Context, store, name string) (io.ReadCloser, error)
// Write replaces the file name with what data yields, at most MaxFileSize bytes, whole or not
// at all: when data or ctx fails, the store stays as it was.
Write(ctx context.Context, store, name string, data io.Reader) (FileInfo, error)
// Mkdir makes the folder name and its missing parents. A folder that exists is no error.
Mkdir(ctx context.Context, store, name string) error
// Remove removes the file or the empty folder name.
Remove(ctx context.Context, store, name string) error
// Rename renames oldName to newName and replaces a file at newName. The server never asks it
// to replace a folder.
Rename(ctx context.Context, store, oldName, newName string) error
}
Files keeps an app's file stores. The server calls it only for a store the app declares, with a name that fs.ValidPath accepts, of at most 1024 bytes, whose elements are at most 255 bytes, hold no control character and do not start with ".aicoded-tmp-", a prefix Files may use for files of its own. A name that Write, Mkdir, Remove or Rename changes is never ".".
Its errors wrap fs.ErrNotExist, fs.ErrExist, fs.ErrPermission or a syscall.Errno, from which the server makes the app's error: a code and a message that names the file as the app named it. Any other error reaches the app as an internal error.
type Mail ¶
type Mail interface {
// Send sends msg from the address from, the app's email.from, once for each idempotency key
// it remembers, and returns the message's id; a repeated key it remembers returns the first
// message's id.
Send(ctx context.Context, from string, msg *runnerv1.SendRequest) (string, error)
// List returns a page of the messages in folder, newest first, and how many it holds.
List(ctx context.Context, folder string, offset, limit int) ([]*runnerv1.MessageSummary, int, error)
// Get returns the message id.
Get(ctx context.Context, id string) (*runnerv1.GetResponse, error)
// Delete deletes the message id.
Delete(ctx context.Context, id string) error
// Move moves the message id to folder.
Move(ctx context.Context, id, folder string) error
}
Mail sends an app's mail and serves its mailbox. The server checks every message against the mail rules and the app's email section first, and every folder, page and message id the app names. A target that keeps no mailbox answers List, Get, Delete and Move with a *connect.Error, E-MAIL-008 with the code Unimplemented.
type Minter ¶
type Minter interface {
// Public returns the key that verifies the tokens it signs.
Public() ed25519.PublicKey
// MintCall returns the token of a call from the app caller to the app callee: on behalf of
// the viewer v, or of the calling app itself when v is nil.
MintCall(v *viewer.Claims, caller, callee string, now time.Time) (string, error)
}
Minter signs the tokens the runner hands apps.
type Options ¶
type Options struct {
// Dir is the folder where the server makes runner.sock, and mysql.sock when the app declares
// sqldb. The app makes app.sock there.
Dir string
// Manifest is the app's permission list.
Manifest manifest.Manifest
// Egress, when not nil, replaces the manifest's egress for the app's requests to outside
// services: a preview reaches only the entries it shares with the app's live release. It may
// hold only entries of the manifest's egress.
Egress []manifest.Egress
// Env is the environment the app runs in: EnvDev, EnvPreview or EnvProduction.
Env string
// Version is the version of the release that Hello reports.
Version string
// ViewerKeys verify the viewer tokens the app accepts. There must be at least one.
ViewerKeys []ed25519.PublicKey
// CSRFKey signs the app's form tokens: an app accepts the tokens of another instance of it
// with the same key. It must be at least MinCSRFKey bytes.
CSRFKey []byte
// the app to do the same with app.sock: for an app that runs as another user. Dir then
// decides who reaches them.
ShareAppSocket bool
// Adopted marks an app that is already running and said Hello to an earlier server. Ready is
// closed at New; nothing else changes.
Adopted bool
// Database is where the proxy on mysql.sock logs in, until SetDatabase changes it. It is set
// exactly when the app declares sqldb.
Database *mysqlproxy.Upstream
// OnDatabaseError, if set, gets each reason the proxy could not open the app's database.
// It may be called from several goroutines at once.
OnDatabaseError func(error)
// OnReady, if set, is called at each Ready the app sends.
OnReady func()
// Providers do the work of the building blocks.
Providers Providers
}
Options configure New.
type Outbound ¶ added in v0.10.0
Outbound sends an app's requests to outside services. The server hands it only a request it checked against egress: https to port 443 of a host egress lists, by its canonical name, with the key egress binds to the host in its header, no hop-by-hop or Host header and a body of at most runnerproto.MaxOutboundBody bytes.
Do resolves and dials the host, follows no redirect and bounds the response body to runnerproto.MaxOutboundBody bytes and the whole request to runnerproto.OutboundDeadline, until the body is closed. A refusal or failure meant for the app is an *errs.Error of the framework's error catalogue with an E-OUT code, returned by Do or by a read of the body; it reaches the app by its code and message. Any other error, and a response without a body, reaches the app as E-OUT-008, without its text.
type Providers ¶
type Providers struct {
// Files keeps the file stores. It must be set when the app declares one.
Files Files
// Mail sends the app's mail and serves its mailbox. It must be set when the app declares email.
Mail Mail
// Secrets holds the values of the app's secrets and settings. It must be set when the app
// declares either.
Secrets Secrets
// Telemetry takes the app's spans. With nil, they are dropped.
Telemetry Telemetry
// Calls carries the app's calls to other apps, as Router does. With nil, every call fails
// with E-RPC-011.
Calls Calls
// Outbound sends the app's requests to outside services. It must be set when the app
// declares egress.
Outbound Outbound
}
Providers do the work of the building blocks on the target the app runs on. The server calls them only with what passed its checks.
A *connect.Error a provider returns reaches the app as it is, so its code and message must be meant for the app. Any other error reaches the app as an internal error, without its text, except the file errors that Files describes.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router carries calls between the apps that one runner serves. It checks every call against the permission lists of both apps and hands the called app a fresh token for the viewer, or for the calling app when there is no viewer.
func NewRouter ¶
NewRouter returns a router that verifies the viewer tokens apps forward and signs call tokens with minter.
func (*Router) Add ¶
Add lets the other apps call the app of m, which listens on appSocket. It closes the connections to the instance of the app it replaces.
func (*Router) Call ¶
func (r *Router) Call(ctx context.Context, caller manifest.Manifest, req *runnerv1.CallRequest, traceparent string) (*runnerv1.CallResponse, error)
Call checks req, a call made by the app of caller, and forwards it to the called app with traceparent. A refusal is a connect error marked as the runner's, whose message is the text of a coded error. An error of the called app passes on only with a code a function may return, and never with the runner's mark.
type Secrets ¶
type Secrets interface {
// Secret returns the value of the secret name, which the app declares.
Secret(ctx context.Context, name string) (string, error)
// Settings returns the values of the settings. Hello hands the app those it declares. It may
// be called from several goroutines at once, and the server never changes the map.
Settings() map[string]string
}
Secrets holds the values of an app's secrets and settings. The server reads a key that egress binds to a host for the app's requests to that host, and keeps its value in memory for up to a minute after each read.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server serves the runner protocol to one app. It knows the app by the socket it listens on, never by anything in a request, and logs nothing, so that an app cannot write to its log.
func New ¶
New starts serving the runner protocol on runner.sock in opts.Dir, and the app's database on mysql.sock there when the app declares sqldb. It refuses options that do not fit the app.
func (*Server) Ready ¶
func (s *Server) Ready() <-chan struct{}
Ready is closed once the app reports that it accepts requests, or at New for an adopted app.
func (*Server) SetDatabase ¶
func (s *Server) SetDatabase(up mysqlproxy.Upstream) error
SetDatabase makes the proxy on mysql.sock log in as up from the next connection the app opens on: sessions already open keep their login. It refuses a server that serves no database.
type Telemetry ¶
type Telemetry interface {
// Export takes spans, and dropped, the number of spans the app or the server dropped since
// the previous export.
Export(ctx context.Context, spans []*runnerv1.Span, dropped uint64)
}
Telemetry takes an app's spans. Each span's trace id is empty or 16 bytes, and its span and parent ids empty or 8 bytes. Its name and error are at most 4 KiB, and it has at most 64 attributes, with keys of at most 256 bytes and values of at most 4 KiB. One export holds at most 1024 spans.