appstore

package
v0.52.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package appstore is filex's side of an app store (docs/APP-PLUGINS.md → Installing from a store, Paid apps; docs/APP-PLUGINS-API.md → The store contract): the stores an administrator trusts, the install links a store hands out, and the licenses of paid apps, which the store issues, keeps and is asked about.

What it never does is install anything by itself. A link opens filex's own install review, filled in; the administrator reads it and presses Install, exactly as for a repository they typed (the SHA-256 protects the administrator, the sandbox protects the user).

Index

Constants

View Source
const (
	CodeBadStore         = "store_invalid"            // the store address is not an origin filex talks to
	CodeStoreRefused     = "store_not_allowed"        // FILEX_APP_STORE_URLS is set and does not list it (an allow list: no trust on first use)
	CodeTrustRequired    = "store_trust_required"     // not trusted yet: the administrator is asked
	CodeKeyChanged       = "store_key_changed"        // trusted, but its keys changed: asked again
	CodeKeyNotConfigured = "store_key_not_configured" // a configured store signed with a key FILEX_APP_STORE_KEYS lacks
	CodeUnreachable      = "store_unreachable"        // the store could not be reached
	CodeBadAnswer        = "store_bad_answer"         // the store answered something filex cannot read
	CodeSignature        = "store_signature_invalid"
	CodeIntentUnknown    = "intent_unknown"        // 404
	CodeIntentGone       = "intent_gone"           // 410: used or expired at the store
	CodeIntentExpired    = "intent_expired"        // its expires_at has passed
	CodeIntentUsed       = "intent_used"           // this filex already used it
	CodeIntentInvalid    = "intent_invalid"        // fields missing or not for this store
	CodeWrongInstance    = "intent_wrong_instance" // made for another filex (filex_origin)
	CodePinMismatch      = "intent_pin_mismatch"
	CodeVersionRollback  = "intent_version_rollback" // older than (or the same as) what is installed
	CodeSourceChanged    = "store_source_changed"    // the app installed came from another store or repository: remove it first
	CodeIntentNotFound   = "intent_session_unknown"  // no reviewed intent under that handle
	CodeLicenseKey       = "license_key_invalid"
)

Error codes the HTTP layer answers with and the panel switches on.

View Source
const (
	KindApp          = "app"
	KindLanguagePack = "language_pack"
)

Intent kinds a link may stand for.

View Source
const (
	ResultValid          = "valid"
	ResultInvalid        = "invalid"
	ResultRevoked        = "revoked"
	ResultExpired        = "expired"
	ResultSeatsExhausted = "seats_exhausted"
	ResultWrongApp       = "wrong_app"
)

License results a store signs (`POST <store>/v1/licenses/verify`).

View Source
const (
	StatusFree         = "free"          // not a paid app
	StatusValid        = "valid"         // the store said valid at its last check
	StatusGrace        = "grace"         // valid, but the store could not be asked since; holds until grace_until
	StatusGraceExpired = "grace_expired" // the store could not be asked, and grace_until has passed
	StatusMissing      = "missing"       // a paid app with no key
	StatusUnverified   = "unverified"    // a key, never confirmed by the store
)

License statuses filex reports (the store's results, plus what filex decides by itself).

View Source
const (
	UseIndex    = "index"
	UseArtifact = "artifact"
	UseLicense  = "license"
)

Key uses: what a store key may sign. A key signs for ONE use, and a signature is accepted only from a key of the use the answer needs: an install link is signed with an `index` key, a license answer with a `license` key. One key leaking does not open the other's door.

View Source
const (
	KeyActive  = "active"
	KeyNext    = "next"
	KeyRetired = "retired"
)

Key statuses in a store's keys.json. A key signs while it is `active`; a `next` key is published before it signs (rotation), a `retired` one is kept to identify old signatures and signs nothing new.

View Source
const (
	SourceAdmin  = "admin"  // an administrator trusted it on first use
	SourceConfig = "config" // FILEX_APP_STORE_URLS + FILEX_APP_STORE_KEYS
)

Trust sources.

Variables

View Source
var (
	ErrUnknownKey       = errors.New("signed by a key this store was not trusted with")
	ErrWrongKeyUse      = errors.New("signed by a key of another use")
	ErrKeyNotActive     = errors.New("signed by a key that is not active")
	ErrSignatureInvalid = errors.New("signature does not verify")
	ErrMalformed        = errors.New("not a signed answer")
)

Signature errors, matched by the callers to pick an error code.

View Source
var ErrNoSecretKey = errors.New("a license key is kept encrypted, and this installation has no FILEX_SECRET_KEY")

ErrNoSecretKey: a license key is kept sealed, which needs FILEX_SECRET_KEY.

View Source
var ErrNotCanonicalizable = errors.New("not canonical JSON material")

ErrNotCanonicalizable is answered for JSON the canonical form cannot carry: a duplicated key, invalid UTF-8, trailing data.

Functions

func Canonical

func Canonical(raw []byte) ([]byte, error)

Canonical is the one byte form a store signs: the JSON value with every object's keys in sorted order (by their UTF-8 bytes; every key of the contract is ASCII), no whitespace, UTF-8 text written as itself, and only `"`, `\` and the control characters escaped - `\b \f \n \r \t` by name, the others as `\u00xx` in lower-case hex. That is what JavaScript's JSON.stringify writes for a string, so a store written in any language that sorts keys produces the same bytes. A number is kept exactly as it was written.

⚠ Not encoding/json's Marshal: it escapes `<`, `>`, `&`, U+2028 and U+2029, which a store that does not would not have signed, and it cannot keep a number's spelling. A duplicated key is refused rather than resolved: two readers that keep different copies would read two different documents under one signature.

func ComparePins

func ComparePins(in *Intent, src *Source) error

ComparePins holds the source to the link: the same app and version, the same manifest bytes, module and interface hashes, and the same declared permissions. Every difference is listed; any one refuses the install.

func Fingerprints

func Fingerprints(keys []Key) []string

Fingerprints is the sorted fingerprint list of keys: what an administrator's approval names, so the keys they saw are the keys pinned.

func InstanceOrigin

func InstanceOrigin(raw string) (string, error)

InstanceOrigin is the one spelling of a filex's own address that a link's filex_origin is compared in: scheme and host in lower case, the default port dropped, no path, no trailing slash. A configured public URL with a base path (https://example.com/filex) is its origin (https://example.com).

func NormalizeOrigin

func NormalizeOrigin(raw string, loopback bool) (string, error)

NormalizeOrigin turns what a link or an administrator names into the one spelling a store is known by - `https://host[:port]`, lower-case, no path, no default port. Plain http is accepted for this machine only and only with loopback (FILEX_PLUGIN_LOOPBACK_SOURCES), the rule every plugin download follows.

func PayloadSHA256

func PayloadSHA256(payload []byte) (string, error)

PayloadSHA256 is the lower-hex sha256 of the payload's canonical form: what the signature is over.

func Prefix

func Prefix(key string) string

Prefix is what of a key the screens, the API and the audit log show: the first six characters at most (a third of a short key), then an ellipsis.

func SHA256Hex

func SHA256Hex(b []byte) string

SHA256Hex is the lower-hex sha256 of b (what a link's pins are written in).

func ValidKey

func ValidKey(s string) bool

ValidKey reports whether s may be a license key: 4-512 printable ASCII characters, no spaces.

func ValidKeyID

func ValidKeyID(id string) bool

ValidKeyID reports whether id may name a store key.

func ValidToken

func ValidToken(t string) bool

ValidToken reports whether an install link's token may be put in a path: the URL-safe characters a store's random token is made of.

func Verify

func Verify(env *Envelope, trusted []Key, use string) error

Verify checks an envelope against the keys a store was trusted with: the key named by key_id must be one of them, of the use the answer needs, and signing; the signature must verify over the canonical payload. Only that one key is tried - a signature from another trusted key of another use is still refused.

Types

type Answer

type Answer struct {
	Result       string     `json:"result"`
	App          string     `json:"app"`
	Licensee     string     `json:"licensee,omitempty"`
	Seats        *int       `json:"seats,omitempty"`
	SeatsUsed    *int       `json:"seats_used,omitempty"`
	ValidUntil   *time.Time `json:"valid_until,omitempty"`
	UpdatesUntil *time.Time `json:"updates_until,omitempty"`
	InstanceID   string     `json:"instance_id"`
	CheckedAt    time.Time  `json:"checked_at"`
	NextCheckBy  time.Time  `json:"next_check_by"`
	GraceUntil   time.Time  `json:"grace_until"`
}

Answer is a license check's signed payload.

type AppView

type AppView struct {
	Status       string     `json:"status"`
	ValidUntil   *time.Time `json:"valid_until,omitempty"`
	UpdatesUntil *time.Time `json:"updates_until,omitempty"`
}

AppView is a license as the app itself reads it (fx.license.get()): the status and its dates - nothing about the key, the store or who holds it. ⚠ Every user who may run apps reads it, a tenant's included, and any token with that right: the licensee (a person's or a company's name) is the administrator's to see (View), not theirs.

type Client

type Client struct {
	HTTP  *http.Client
	Guard *netguard.Policy
	// UserAgent names this filex.
	UserAgent string
}

Client talks to stores: one guarded HTTP client (netguard: public addresses only, judged after DNS), small size ceilings, JSON only - and no redirect followed (netguard.APIClient): a license check carries the key, a completion the instance id, and a redirect must not carry them to another host. (The app's own files - manifest, module, interface, from a GitHub release that redirects to its asset host - are downloaded by wasmplugin's DownloadClient, which follows redirects and sends nothing.)

func NewClient

func NewClient(policy netguard.Policy, userAgent string) *Client

NewClient builds the guarded, non-redirecting client under policy.

func (*Client) Complete

func (c *Client) Complete(ctx context.Context, origin, token, instanceID, result string) error

Complete tells the store how an install link ended: `POST <store>/v1/install/{token}/complete`.

func (*Client) Intent

func (c *Client) Intent(ctx context.Context, origin, token string) (*Envelope, error)

Intent reads `GET <store>/v1/install/{token}` as an envelope. 404 and 410 are said as such.

func (*Client) Keys

func (c *Client) Keys(ctx context.Context, origin string) (*KeySet, error)

Keys reads `<store>/v1/keys.json`, validated.

func (*Client) VerifyLicense

func (c *Client) VerifyLicense(ctx context.Context, origin string, req LicenseRequest) (*Envelope, error)

VerifyLicense asks `POST <store>/v1/licenses/verify` and answers the envelope.

type Envelope

type Envelope struct {
	Payload   json.RawMessage `json:"payload"`
	KeyID     string          `json:"key_id"`
	Signature string          `json:"signature"`
}

Envelope is a signed answer: `{"payload": {...}, "key_id": "...", "signature": "<hex>"}`. signature = ed25519 over the lower-hex sha256 of Canonical(payload) - the rule plugin.VerifyDetached applies to a module.

func ParseEnvelope

func ParseEnvelope(raw []byte) (*Envelope, error)

ParseEnvelope reads a signed answer, refusing one without its three parts.

type Error

type Error struct {
	Code    string         `json:"error"`
	Message string         `json:"message"`
	Detail  map[string]any `json:"detail,omitempty"`
}

Error is a refusal with a code; Detail carries what the panel draws (the fingerprints of a store to trust, the pins that differ).

func AsError

func AsError(err error) (*Error, bool)

AsError unwraps an *Error.

func (*Error) Error

func (e *Error) Error() string

type Holder

type Holder interface {
	SetLicenseHold(app, reason string)
}

Holder is what a license decides about an installed app: held (it does not run, and says why) or not. wasmplugin.Registry is the one in service.

type Intent

type Intent struct {
	Store          string    `json:"store"`
	TokenID        string    `json:"token_id"`
	App            string    `json:"app"`
	Kind           string    `json:"kind"`
	Version        string    `json:"version"`
	Repo           string    `json:"repo"`
	Ref            string    `json:"ref"`
	Commit         string    `json:"commit"`
	ManifestSHA256 string    `json:"manifest_sha256"`
	WasmSHA256     string    `json:"wasm_sha256,omitempty"`
	UISHA256       string    `json:"ui_sha256,omitempty"`
	Permissions    []string  `json:"permissions"`
	FilexRange     string    `json:"filex_range"`
	Paid           bool      `json:"paid"`
	LicenseKey     string    `json:"license_key,omitempty"`
	ExpiresAt      time.Time `json:"expires_at"`
	// FilexOrigin is the filex the link was made for (the store signs it):
	// another filex refuses the link (CodeWrongInstance). That stops a link
	// opened by mistake on the wrong filex, and a link sent to an
	// administrator of another filex to get an app installed there. It is
	// no protection against the administrator of the filex receiving it:
	// without FILEX_PUBLIC_URL the filex's own origin is the request's Host
	// (and X-Forwarded-Proto), which that person controls.
	FilexOrigin string `json:"filex_origin"`
}

Intent is what an install link stands for, as the store signed it (`GET <store>/v1/install/{token}`, signed with an `index` key).

type Key

type Key struct {
	ID      string `json:"id"`
	Use     string `json:"use"`
	Ed25519 string `json:"ed25519"`
	Status  string `json:"status,omitempty"`
}

Key is one key of a store's keys.json (`{"keys": [...]}`), and what a trusted store keeps of the keys an administrator saw.

func (Key) Fingerprint

func (k Key) Fingerprint() string

Fingerprint is what an administrator compares with what the store publishes: the lower-hex sha256 of the 32 raw key bytes. "" when the material does not parse.

func (Key) PublicKey

func (k Key) PublicKey() (ed25519.PublicKey, error)

PublicKey parses the key's material (hex or base64, 32 bytes).

type KeySet

type KeySet struct {
	Keys []Key `json:"keys"`
}

KeySet is a store's keys.json.

func (*KeySet) TrustedKeys

func (s *KeySet) TrustedKeys() []Key

TrustedKeys is the part of a keys.json filex pins when a store is trusted: the keys that sign what filex reads (install links, license answers) and may sign now or soon. A retired key is not pinned; an artifact key is a module's business (FILEX_PLUGIN_TRUSTED_KEYS), not this package's.

func (*KeySet) Validate

func (s *KeySet) Validate() error

Validate checks a keys.json: every key has an id, a known use and material that parses; no id twice. A store whose list does not hold together is refused whole rather than read in part.

type KeyView

type KeyView struct {
	ID          string `json:"id"`
	Use         string `json:"use"`
	Status      string `json:"status,omitempty"`
	Fingerprint string `json:"fingerprint"`
}

KeyView is a key as the panel shows it.

type License

type License struct {
	App       string `json:"app"`
	Store     string `json:"store"`
	KeySealed string `json:"key_sealed,omitempty"`
	KeyPrefix string `json:"key_prefix,omitempty"`
	// Last is the store's last signed answer (any result); LastValid the
	// last one that said valid. Kept whole: the signature travels with them.
	Last      *Envelope `json:"last,omitempty"`
	LastValid *Envelope `json:"last_valid,omitempty"`
	// LastAttemptAt / LastError: the last check, when it did not end in a
	// signed answer (the store unreachable, a bad answer). Cleared by one
	// that did.
	LastAttemptAt time.Time `json:"last_attempt_at,omitempty"`
	LastSuccessAt time.Time `json:"last_success_at,omitempty"`
	LastError     string    `json:"last_error,omitempty"`
	LastErrorCode string    `json:"last_error_code,omitempty"`
	// Status is the last status decided, for telling a change (audit).
	Status    string    `json:"status,omitempty"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

License is a paid app's license as filex keeps it (app_store_state `license:<app>`): the key sealed, its prefix for the screens, and the store's last signed answers - the evidence a status is judged from.

type LicenseRequest

type LicenseRequest struct {
	Key          string `json:"key"`
	App          string `json:"app"`
	InstanceID   string `json:"instance_id"`
	FilexVersion string `json:"filex_version,omitempty"`
}

LicenseRequest is the body of a license check.

type Mismatch

type Mismatch struct {
	Field  string `json:"field"`
	Link   string `json:"link"`
	Source string `json:"source"`
}

Mismatch is one pin the source does not keep.

type Options

type Options struct {
	Store  StateStore
	Client *Client
	// Box seals license keys (the installation's FILEX_SECRET_KEY). Without a
	// key a license cannot be kept, and a paid app is refused at install.
	Box *secretbox.Box
	// Loopback admits a store on this machine over plain http
	// (FILEX_PLUGIN_LOOPBACK_SOURCES): development and tests.
	Loopback bool
	// ConfigStores (FILEX_APP_STORE_URLS) are stores the operator trusts by
	// configuration; ConfigKeys (FILEX_APP_STORE_KEYS) the keys they sign
	// with. A configured store is never put to an administrator: its keys are
	// these, and a key that is not among them is refused.
	ConfigStores []string
	ConfigKeys   []string
	// ConfigStoresSet: FILEX_APP_STORE_URLS was given a value, also one that
	// names no store - the allow list is in force either way.
	ConfigStoresSet bool
	FilexVersion    string
	Holder          Holder
	Log             *slog.Logger
	// Now is the wall clock and Mono the time elapsed since start on a
	// monotonic clock - for tests; zero values are the real clocks.
	Now  func() time.Time
	Mono func() time.Duration
}

Options configure a Service.

type Pending

type Pending struct {
	Handle  string
	Origin  string
	Token   string
	Intent  *Intent
	UserID  int64
	Created time.Time
}

Pending is a link that passed every check and whose review the administrator is reading. The browser holds its Handle, never the link's license key.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the store side of filex: trust, install links, licenses.

func New

func New(o Options) *Service

New builds the service. Configured stores that do not parse are logged and left out, never guessed at.

func (*Service) AppLicense

func (s *Service) AppLicense(ctx context.Context, app string) *AppView

AppLicense is what the app reads about itself (fx.license.get()).

func (*Service) ApplyHolds

func (s *Service) ApplyHolds(ctx context.Context)

ApplyHolds re-judges every license (time moves; a grace ends) and keeps the clock's reading.

func (*Service) Approve

func (s *Service) Approve(ctx context.Context, origin string, fingerprints []string, actorID *int64, actorName string) (*TrustView, error)

Approve trusts origin with the keys it publishes NOW, provided they are the keys the administrator was shown (fingerprints, as Fingerprints spells them): a store whose keys changed while the page was open is asked about again, never trusted with keys nobody saw.

A configured store is not the administrator's to trust or distrust.

func (*Service) Check

func (s *Service) Check(ctx context.Context, app string, actorID *int64) (*View, error)

Check asks the store about app's license now and answers the result. actorID is the administrator who pressed "Verify now" (nil: the daily check).

func (*Service) Configured

func (s *Service) Configured(origin string) bool

Configured reports whether origin is a store trusted by configuration.

func (*Service) Drop

func (s *Service) Drop(handle string)

Drop forgets a reviewed link.

func (*Service) Finish

func (s *Service) Finish(ctx context.Context, p *Pending, result string, actorID *int64)

Finish ends a reviewed link: the store is told (installed / cancelled) and the link is recorded as used here. A store that cannot be told is logged; the install stands.

func (*Service) Forget

func (s *Service) Forget(ctx context.Context, app string)

Forget removes what filex keeps about an app installed from a store (the app was removed): its license - and the hold goes with it - and where it came from.

func (*Service) Hold

func (s *Service) Hold(origin, token string, in *Intent, userID int64) *Pending

Hold keeps a reviewed link under a new handle for the administrator who reviewed it.

func (*Service) InstalledFrom

func (s *Service) InstalledFrom(ctx context.Context, app string) (string, error)

InstalledFrom answers the store an installed app came from: the one its install link was signed by, or the one its license is with; "" when it was not installed from a store.

func (*Service) InstanceID

func (s *Service) InstanceID(ctx context.Context) (string, error)

InstanceID is this installation's opaque id, made once: what a store counts a license's seats by. Random, not derived from anything about the server or its people.

func (*Service) LicenseOf

func (s *Service) LicenseOf(ctx context.Context, app string) (*View, error)

LicenseOf is app's license for the panel; nil when app is not a paid app.

func (*Service) Licenses

func (s *Service) Licenses(ctx context.Context) []*View

Licenses is every paid app's license, for the panel's warning band.

func (*Service) ListTrust

func (s *Service) ListTrust(ctx context.Context) ([]TrustView, error)

ListTrust is every trusted store: configured ones first, then the ones an administrator trusted.

func (*Service) LockApp

func (s *Service) LockApp(app string) (unlock func())

LockApp serialises the store installs of one app name (the license, the install, the record of where it came from): answers the unlock.

func (*Service) Loopback

func (s *Service) Loopback() bool

Loopback reports whether plain-http loopback stores are admitted.

func (*Service) Now

func (s *Service) Now() time.Time

Now is the latest time filex has proof of, across every store (clock.go): for the panel and the log. A license is judged by its own store's proof.

func (*Service) Pending

func (s *Service) Pending(handle string, userID int64) (*Pending, error)

Pending answers the reviewed link under handle for this administrator.

func (*Service) PutBack

func (s *Service) PutBack(p *Pending)

PutBack returns a taken review (its install failed), unless it has expired meanwhile.

func (*Service) ReadIntent

func (s *Service) ReadIntent(ctx context.Context, origin, token, instance string) (*Intent, error)

ReadIntent fetches and checks an install link: the store must be trusted (or an administrator is asked, CodeTrustRequired / CodeKeyChanged), the answer signed by one of its `index` keys, the payload this store's, made for this filex (instance, its own origin: InstanceOrigin), naming its commit and pinning its manifest, not expired and not used here before.

func (*Service) Remove

func (s *Service) Remove(ctx context.Context, origin string, actorID *int64) (bool, error)

Remove stops trusting a store an administrator trusted. Its install links are refused from then on, and the licenses it issued are not checked against it any more (they hold until their grace ends).

func (*Service) Require

func (s *Service) Require(ctx context.Context, app, store, key string, actorID *int64) error

Require makes app a paid app licensed by store, with key ("" = none yet). Called at a paid install BEFORE the app is installed: the hold is in place before the app could run, and lifted by a valid answer.

func (*Service) RequireUndoable

func (s *Service) RequireUndoable(ctx context.Context, app, store, key string, actorID *int64) (*Undo, error)

RequireUndoable is Require, answering what to Undo when the install it prepared fails.

func (*Service) Run

func (s *Service) Run(ctx context.Context)

Run is the license loop: a round a minute after start, then hourly. On the way out (ctx done: a clean shutdown) it keeps the proven time once more, marked clean, so the next start owes no restart allowance.

func (*Service) SetKey

func (s *Service) SetKey(ctx context.Context, app, key string, actorID *int64) (*View, error)

SetKey replaces a paid app's key and asks the store about it at once.

func (*Service) Start

func (s *Service) Start(ctx context.Context)

Start loads what the service keeps between runs (every store's proven time) and applies every paid app's hold before anything is served. After a run that did not stop cleanly, the proven times move on by the restart allowance first (clock.go); ApplyHolds keeps that at once, marked not clean, so a run killed before its first round still owes it.

func (*Service) Take

func (s *Service) Take(handle string, userID int64) (*Pending, error)

Take answers the reviewed link under handle for this administrator AND removes it, in one step: of two installs of one review at once (a double click, or on purpose) only one gets it; the other is told the review is not open (CodeIntentNotFound). An install that fails before anything is installed puts it back (PutBack), so the administrator can try again.

func (*Service) Tick

func (s *Service) Tick(ctx context.Context)

Tick is one round of the license loop: ask the store about every license that is due, then re-judge them all.

func (*Service) TrustStatus

func (s *Service) TrustStatus(ctx context.Context, origin string) string

TrustStatus answers whether origin is trusted, for the panel, without asking the store: "config", "admin" or "".

func (*Service) Undo

func (s *Service) Undo(ctx context.Context, u *Undo)

Undo takes back what RequireUndoable did for an install that failed: the row it CREATED is removed (and its hold lifted) - that row only, never one another install created: of two installs of one app, the loser must not forget the winner's license - and a row it CHANGED (a paid upgrade's new key) is put back as it was, key, answers and hold.

type Source

type Source struct {
	ManifestBytes []byte
	Name          string
	Version       string
	Kind          string
	// DeclaredPermissions is the manifest's own `permissions` list (not the
	// ones filex derives from its interface).
	DeclaredPermissions []string
	WasmSHA256          string // the manifest's wasm.sha256 (the module filex fetched hashes to it)
	UISHA256            string // the manifest's ui.bundle.sha256
}

Source is what filex fetched for a link from the app's repository: the manifest's bytes and what it says.

type StateStore

type StateStore interface {
	GetAppStoreState(ctx context.Context, key string) (string, bool, error)
	PutAppStoreState(ctx context.Context, key, value string) error
	DeleteAppStoreState(ctx context.Context, key string) (bool, error)
	ListAppStoreState(ctx context.Context, prefix string) (map[string]string, error)
	InsertAuditEntry(ctx context.Context, e *model.AuditEntry) error
}

StateStore is the part of db.Store this package uses: its own rows (app_store_state, migration 00081) and the audit log.

type Trust

type Trust struct {
	Origin         string    `json:"origin"`
	Keys           []Key     `json:"keys"`
	Source         string    `json:"source"`
	ApprovedBy     *int64    `json:"approved_by,omitempty"`
	ApprovedByName string    `json:"approved_by_name,omitempty"`
	ApprovedAt     time.Time `json:"approved_at"`
	// UpdatedAt: when the key list last narrowed by itself (a key retired or
	// gone at the store) or a status moved (next → active).
	UpdatedAt time.Time `json:"updated_at,omitempty"`
}

Trust is a store filex accepts install links and license answers from, and the keys it accepts them signed with - the keys the administrator SAW when they trusted it (or the operator configured). A key the store publishes later is not among them until somebody approves it again.

type TrustView

type TrustView struct {
	Origin         string    `json:"origin"`
	Source         string    `json:"source"`
	Keys           []KeyView `json:"keys"`
	ApprovedByName string    `json:"approved_by_name,omitempty"`
	ApprovedAt     time.Time `json:"approved_at,omitempty"`
}

TrustView is a trusted store as the panel lists it.

type Undo

type Undo struct {
	// contains filtered or unexported fields
}

Undo is what Require changed, for an install that then failed (Service.Undo).

type View

type View struct {
	App           string     `json:"app"`
	Required      bool       `json:"required"`
	Status        string     `json:"status"`
	Held          bool       `json:"held"`
	Store         string     `json:"store,omitempty"`
	KeyPrefix     string     `json:"key_prefix,omitempty"`
	Licensee      string     `json:"licensee,omitempty"`
	Seats         *int       `json:"seats,omitempty"`
	SeatsUsed     *int       `json:"seats_used,omitempty"`
	ValidUntil    *time.Time `json:"valid_until,omitempty"`
	UpdatesUntil  *time.Time `json:"updates_until,omitempty"`
	CheckedAt     *time.Time `json:"checked_at,omitempty"`
	NextCheckBy   *time.Time `json:"next_check_by,omitempty"`
	GraceUntil    *time.Time `json:"grace_until,omitempty"`
	LastAttemptAt *time.Time `json:"last_attempt_at,omitempty"`
	LastErrorCode string     `json:"last_error_code,omitempty"`
	LastError     string     `json:"last_error,omitempty"`
	// StoreTrusted: false when the store that issued the license is no
	// longer trusted here (it cannot be asked any more).
	StoreTrusted bool `json:"store_trusted"`
}

View is a license as the administrator's panel shows it. The key itself is never in it - its prefix only.

Directories

Path Synopsis
Package storetest is a fake app store for tests: the `/v1/` paths a filex reads (docs/APP-PLUGINS-API.md → The store contract), signing exactly as a store must - ed25519 over the lower-hex sha256 of the canonical payload - and every answer adjustable mid-test (a tampered signature, a 410, a key that changes, a store that cannot be reached).
Package storetest is a fake app store for tests: the `/v1/` paths a filex reads (docs/APP-PLUGINS-API.md → The store contract), signing exactly as a store must - ed25519 over the lower-hex sha256 of the canonical payload - and every answer adjustable mid-test (a tampered signature, a 410, a key that changes, a store that cannot be reached).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL