Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

v0 source pure

Package app is an upgradeable realm in a handful of forwarding functions.

Overview

Package app is an upgradeable realm in a handful of forwarding functions.

It answers a narrower question than upgradeable does. That package gives you a proxy and leaves the shape of your realm to you: your interface, your state, your entry points, all of them permanent. This one makes a different trade — it fixes the shape once, generically, so that nothing about your application is permanent except the path.

Example
 1package todo
 2
 3import "gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/app/v0"
 4
 5const Admin = address("g1…")
 6
 7var a = app.New(app.Owner(Admin))
 8
 9func init(cur realm)                              { a.Bind(0, cur) }
10func Call(cur realm, verb, payload string) string { return a.Call(0, cur, verb, payload) }
11func Register(cur realm, h app.Handler)           { a.Register(0, cur, h) }
12func Manage(cur realm, verb, arg string) string   { return a.Admin(0, cur, verb, arg) }
13func Store(_ int, rlm realm) *app.Store           { return a.Store(0, rlm) }
14func Render(path string) string                   { return a.Render(path) }

That realm never needs to change again. Its API is Handler's verb set, which is data; its state is a Store, whose keys are data. A later implementation adds verbs and keys freely, and callers reach them through the same Call.

Why the forwards cannot be avoided

Those six lines are the irreducible minimum. A `p/` package cannot declare crossing functions, and `MsgCall` reaches a function by name at a realm path — so every entry point a user calls has to be declared in your realm. What a pure package can do is make each one a single forward, which is what the methods below are shaped for. Copy the block; only Admin changes.

What you give up

Typed entry points. `Call(cur, "transfer", payload)` is one signature forever, so gnoweb shows one function, arguments are strings you encode yourself, and a misspelled verb is a runtime panic rather than a compile-time error. If your API is small and you can name it now, declare it with upgradeable and keep your types.

The two compose: an app realm can carry hand-written typed entry points beside Call, forwarding to Proxy() for dispatch. Start generic, add typed entry points for the verbs that settle.

Constants 1

Variables 1

var ErrNoHandler, ErrNotAHandler, ErrStaleRealm, ErrNotBound, ErrForbidden, ErrUnknownVerb, ErrNoSchema

 1var (
 2	// ErrNoHandler is raised when the app has no accepted implementation.
 3	ErrNoHandler = errors.New("app: no handler is live")
 4
 5	// ErrNotAHandler is raised when the live implementation does not satisfy
 6	// Handler. Register admits only a Handler, so this is unreachable in
 7	// practice and cheap to rule out.
 8	ErrNotAHandler = errors.New("app: live implementation does not satisfy Handler")
 9
10	// ErrStaleRealm is raised when the threaded realm value is not the
11	// caller's live cur.
12	ErrStaleRealm = errors.New("app: realm value is not the caller's live cur")
13
14	// ErrNotBound is raised when Bind has not run, so the app does not know
15	// its own path and cannot tell its callers apart.
16	ErrNotBound = errors.New("app: Bind has not been called from init")
17
18	// ErrForbidden is raised when a realm that is neither the app nor a
19	// registered extension asks for the store.
20	ErrForbidden = errors.New("app: store is not reachable from this realm")
21
22	// ErrUnknownVerb is raised by Admin for a verb it does not implement.
23	ErrUnknownVerb = errors.New("app: unknown admin verb")
24
25	// ErrNoSchema is raised when a handler declares no verbs. An app whose
26	// API cannot be enumerated is the thing this package exists to avoid.
27	ErrNoSchema = errors.New("app: a handler must declare at least one verb")
28)
source

Functions 9

func Encode

1func Encode(v Verb, values ...string) string
source

Encode builds a payload for v from values given in declaration order, validating each against its declared kind. Use it when one realm calls another app, so an encoding mistake fails where it was made.

func Governed

1func Governed(paths ...string) upgradeable.Authority
source

Governed returns an authority for one or more governance realm paths.

func Owner

1func Owner(addr address) upgradeable.Authority
source

Owner returns an authority for a single address. Re-exported so a realm using this package need not import upgradeable as well.

func New

1func New(auth upgradeable.Authority) *App
source

New returns an App whose implementations must be deployed under the app realm's own path.

func NewOpen

1func NewOpen(auth upgradeable.Authority) *App
source

NewOpen returns an App that accepts implementations from any realm. The authority still decides which one serves.

func Opt

1func Opt(name string, kind Kind, doc string) Field
source

Opt declares an optional parameter. Optional parameters must come last, and appending one is the only way to add a parameter to a verb without breaking callers.

func P

1func P(name string, kind Kind, doc string) Field
source

P declares a required parameter.

func NewSchema

1func NewSchema(verbs ...Verb) *Schema
source

NewSchema validates and sorts the declarations. It panics on a schema that could not be served correctly: a blank or duplicate name, an unknown kind, a required parameter after an optional one, or a duplicate parameter name. Those are all authoring mistakes, and a handler carrying one should fail to deploy rather than mis-describe itself forever.

func Op

1func Op(name, doc string, result Kind, params ...Field) Verb
source

Op declares a verb.

Types 8

type App

struct
1type App struct {
2	proxy *upgradeable.Proxy
3	data  *kv
4	self  string
5}
source

App is the proxy, the state and the dispatch, in one object to hold at realm level.

Methods on App

func Admin

method on App
1func (a *App) Admin(_ int, rlm realm, verb string, arg string) string
source

Admin is the whole governance surface behind one entry point, so a realm using this package declares one function instead of one per governance action.

Example
 1accept <path>     make a registered candidate live, unless its schema
 2                  would break a caller of the one it replaces
 3accept-breaking <path>
 4                  the same, allowing a breaking schema change
 5withdraw <path>   drop a candidate
 6rollback          restore the previous handler
 7forget            drop the rollback history
 8freeze            end upgradeability, permanently
 9extend <path>     let another realm act as this one
10unextend <path>   revoke that
11owner <address>   hand authority to an address
12govern <paths>    hand authority to governance realms (comma-separated)

Every one of them is authority-gated by the proxy, except that a realm may always withdraw its own candidate. Returns "ok", or panics.

func Authority

method on App
1func (a *App) Authority() string
source

Authority describes who may upgrade.

func Bind

method on App
1func (a *App) Bind(_ int, rlm realm)
source

Bind teaches the app its own path, which is what every later caller check compares against. Call it once, from your realm's init:

Example
1func init(cur realm) { a.Bind(0, cur) }

It is separate from New because a package-level var initializer runs before there is a realm frame to read the path from.

func Call

method on App
1func (a *App) Call(_ int, rlm realm, verb string, payload string) string
source

Call decodes payload against the live handler's declaration for verb, and dispatches.

An unknown verb, a payload with the wrong number of fields, or a field that does not parse as its declared kind are all refused here, before the handler runs, with a message naming the verb and its signature.

func Extensions

method on App
1func (a *App) Extensions() []string
source

Extensions returns the realms permitted to act as this app.

func Frozen

method on App
1func (a *App) Frozen() bool
source

Frozen reports whether upgradeability has ended.

func HistoryPaths

method on App
1func (a *App) HistoryPaths() []string
source

HistoryPaths returns the handlers already served, oldest first. A non-empty result is what makes rollback available.

func LivePath

method on App
1func (a *App) LivePath() string
source

LivePath returns the realm path of the handler currently serving, or "".

func PendingPaths

method on App
1func (a *App) PendingPaths() []string
source

PendingPaths returns the candidate handler paths awaiting acceptance.

func Proxy

method on App
1func (a *App) Proxy() *upgradeable.Proxy
source

Proxy exposes the underlying proxy, for realms that want typed entry points beside Call or their own Render.

func Register

method on App
1func (a *App) Register(_ int, rlm realm, h Handler)
source

Register nominates the calling realm's handler. Forward it from your realm and call it from the implementation's init.

A handler that declares no verbs is refused here rather than at first call: an app whose API cannot be enumerated is the thing this package exists to avoid.

func Render

method on App
1func (a *App) Render(_ string) string
source

Render is a gnoweb page describing the app's current wiring: what serves, what could, what it used to, who decides, and how much state there is.

func Schema

method on App
1func (a *App) Schema() *Schema
source

Schema returns the live handler's declaration, or nil if nothing is live.

func SchemaJSON

method on App
1func (a *App) SchemaJSON() string
source

SchemaJSON renders the API for tooling: enough to build a form without reading any Gno source.

func Signature

method on App
1func (a *App) Signature(verb string) string
source

Signature returns one verb's declaration, or "" if it is not answered.

func Store

method on App
1func (a *App) Store(_ int, rlm realm) *Store
source

Store hands out a Store accessor to code running on this app's cur: the live handler, and any realm the authority has registered as an extension.

A realm holding a perfectly valid cur of its own is refused, because that cur carries its own path. And because the accessor carries the cur and re-checks it on every operation, the grant cannot outlive this call frame: a handler that stashes the accessor aborts the transaction (a realm value cannot be persisted), so rollback and freeze can no longer be undermined by a captured reference. See Store.

func Supports

method on App
1func (a *App) Supports(verb string) bool
source

Supports reports whether the live handler answers verb.

func Verbs

method on App
1func (a *App) Verbs() []string
source

Verbs enumerates the API. This is the query a caller makes first.

type Args

struct
1type Args struct {
2	verb   Verb
3	values []string
4}
source

Args is a decoded, validated payload. A handler receives one and reads fields by name; the app has already checked that every field is present and parses as its declared kind, so the accessors do not return errors.

Methods on Args

func Address

method on Args
1func (a *Args) Address(name string) address
source

Address returns an address parameter, or the zero address if it was optional and omitted.

func Bool

method on Args
1func (a *Args) Bool(name string) bool
source

Bool returns a bool parameter, or false if it was optional and omitted.

func Has

method on Args
1func (a *Args) Has(name string) bool
source

Has reports whether an optional parameter was supplied.

func Int

method on Args
1func (a *Args) Int(name string) int64
source

Int returns an int parameter, or 0 if it was optional and omitted.

func Raw

method on Args
1func (a *Args) Raw() []string
source

Raw returns the decoded fields in declaration order, for a handler that would rather loop than name them.

func Signature

method on Args
1func (a *Args) Signature() string
source

Signature returns the declaration this payload was checked against, which is worth putting in a panic message.

func String

method on Args
1func (a *Args) String(name string) string
source

String returns a string parameter, or "" if it was optional and omitted.

func Verb

method on Args
1func (a *Args) Verb() string
source

Verb returns the name of the verb being called.

type Field

struct
1type Field struct {
2	Name string
3	Kind Kind
4	Doc  string
5	Opt  bool // may be omitted, and only at the end of the parameter list
6}
source

Field is one parameter of a verb.

type Handler

interface
 1type Handler interface {
 2	// Schema declares the verbs this handler answers. It must not be nil or
 3	// empty, and it should be a package-level value rather than rebuilt per
 4	// call — the app reads it on every dispatch.
 5	Schema() *Schema
 6
 7	// Handle answers args.Verb(). Every declared parameter is present and
 8	// parses as its kind by the time this runs, so the accessors on Args do
 9	// not return errors.
10	Handle(_ int, rlm realm, args *Args) string
11}
source

Handler is what an implementation realm implements.

Two methods. [Handler.Schema] declares the API as data — a verb set rather than a method set, which is what lets it grow past the realm that serves it. [Handler.Handle] answers one call, having been handed a payload the app has already decoded and checked against that declaration.

Panic to fail; panics cross the realm boundary and abort the transaction.

type Kind

ident
1type Kind string
source

Kind is the type of one payload field. The set is deliberately small: these are the types that survive a string encoding without ambiguity.

Methods on Kind

func Valid

method on Kind
1func (k Kind) Valid() bool
source

Valid reports whether k is a known kind.

type Schema

struct
1type Schema struct {
2	verbs []Verb
3}
source

Schema is a handler's whole API, sorted by verb name.

Methods on Schema

func Diff

method on Schema
1func (s *Schema) Diff(prev *Schema) []string
source

Diff reports what upgrading from prev to s would break for existing callers. An empty result means every payload that worked before still works.

Breaking: a verb disappears, a parameter disappears or changes kind or position, a new required parameter appears, or a result kind changes. Not breaking: a new verb, a new optional parameter at the end, new documentation.

func JSON

method on Schema
1func (s *Schema) JSON() string
source

JSON renders the schema for tooling: a client can build a form from this without reading any Gno source.

func Lookup

method on Schema
1func (s *Schema) Lookup(name string) (Verb, bool)
source

Lookup returns the verb by name.

func Markdown

method on Schema
1func (s *Schema) Markdown() string
source

Markdown renders the API as a table, for Render.

func Names

method on Schema
1func (s *Schema) Names() []string
source

Names returns the verb names, sorted.

func Signature

method on Schema
1func (s *Schema) Signature(name string) string
source

Signature renders one verb, or "" if it is not declared.

func Verbs

method on Schema
1func (s *Schema) Verbs() []Verb
source

Verbs returns a copy of the declarations.

type Store

struct
1type Store struct {
2	app *App
3	rlm realm
4}
source

Store is the handler-facing accessor. It is NOT the data -- it is a permission to touch it, bound to the realm value it was issued for.

This is what closes the capability leak. The old design handed out a raw pointer to the data, which a handler could stash in a package variable and keep using after it was rolled back or the realm was frozen. A Store holds the caller's realm value and re-checks it on every operation, so:

  • It cannot be stashed. Assigning it to realm state persists the realm value it carries, which the VM refuses ("cannot persist realm value"), aborting the transaction. The capability cannot outlive the call.
  • It cannot be replayed. Even held transiently, a Store whose frame has returned fails IsCurrent() on its next use.

So the grant lives and dies with the exact call frame that obtained it, which is the only window in which the caller is genuinely the live handler (or a registered extension). See App.Store.

Methods on Store

func AddInt

method on Store
1func (s *Store) AddInt(k string, delta int64) int64
source

AddInt adds delta to k and returns the new value.

func Delete

method on Store
1func (s *Store) Delete(k string) bool
source

Delete removes k, reporting whether it was there.

func Get

method on Store
1func (s *Store) Get(k string) string
source

Get returns the value at k, or "" if absent. An absent key and a key set to "" read the same; use Has to tell them apart.

func GetInt

method on Store
1func (s *Store) GetInt(k string) int64
source

GetInt reads k as an int64, returning 0 if it is absent or unparsable.

func Has

method on Store
1func (s *Store) Has(k string) bool
source

Has reports whether k is present.

func Keys

method on Store
1func (s *Store) Keys() []string
source

Keys returns a copy of the keys, in order.

func KeysWithPrefix

method on Store
1func (s *Store) KeysWithPrefix(prefix string) []string
source

KeysWithPrefix returns the keys under a prefix, in order. Prefixes are how a Store holds more than one collection: "task/1", "task/2", "meta/owner".

func Len

method on Store
1func (s *Store) Len() int
source

Len returns the number of keys.

func Set

method on Store
1func (s *Store) Set(k, v string)
source

Set writes v at k.

func SetInt

method on Store
1func (s *Store) SetInt(k string, v int64)
source

SetInt writes an int64 at k.

type Verb

struct
1type Verb struct {
2	Name   string
3	Doc    string
4	Result Kind
5	Params []Field
6}
source

Verb is one operation's signature.

Methods on Verb

func Required

method on Verb
1func (v Verb) Required() int
source

Required returns how many leading parameters must be supplied.

func Signature

method on Verb
1func (v Verb) Signature() string
source

Signature renders the verb as a one-line declaration:

Example
1add(text:string, urgent:bool?) -> int

Imports 6

Source Files 5