Documentation
¶
Overview ¶
Package wasmplugin runs filex "app plugins": WebAssembly modules executed inside the filex process by wazero (through the Extism host SDK), with no access to the host's filesystem, network, environment or database — only to the host functions filex exposes, each gated by a permission the administrator granted at install.
Why in-process wasm and not a subprocess or a sidecar ¶
The storage-plugin subsystem (internal/plugin) starts a plugin BINARY in filex's own container: same filesystem, same environment, FILEX_SECRET_KEY in reach. That is fine for a driver the operator built, and unacceptable for third-party code a user installs from a URL. A container per plugin needs a Docker socket somewhere; an OS sandbox is strong on Linux and absent on the other two platforms filex ships for. A wasm sandbox is the same on all three, costs no extra process, and cannot reach anything it is not handed.
What the runtime guarantees ¶
- wazero's compiler backend only (amd64/arm64). The interpreter is refused, not tolerated: measured elsewhere, it takes every core for minutes.
- A fresh instance per call: no state survives between users or jobs, memory is returned when the call ends.
- A memory ceiling (Manifest.MemoryPages) and a wall-clock budget per call; the instance is torn down when either is hit.
- No WASI filesystem preopens, no environment, no arguments.
- Compiled modules are cached on disk (WithCompilationCache), so a 20–50 MB Go module is compiled once per host, not once per boot.
Index ¶
- Constants
- Variables
- func ArchSupported() bool
- func CheckOpen(p *Installed, s *wire.Surface) error
- func DecodeInstallBody(b []byte) (map[string]json.RawMessage, error)
- func EncodeJobText(t wire.Text) string
- func ExposedFiles(sh *model.Share) []wire.FileRef
- func IsCode(err error, code string) bool
- func JobText(stored, lang string) string
- func JobTimeout(l wire.Limits) int
- func LocalizeNote(note, lang string) string
- func Matches(a wire.Applies, items []Item) bool
- func NewJobID() string
- func NextTickBoundary(now time.Time) time.Time
- func OutputOverride(params map[string]any) *wire.Output
- func ParseCatalogue(b []byte) ([]string, error)
- func PersonalStateKeys(keys []string, userID int64) []string
- func PublicURL(base, token string) string
- func SanitizeSurface(s *wire.Surface)
- func SetOutputOverride(params map[string]any, o *wire.Output)
- func TickTimeout(m *Manifest) int
- func ValidOutputMode(m string) bool
- func ValidScheduleKey(key string) bool
- func WithActorIP(ctx context.Context, ip string) context.Context
- func WithScope(ctx context.Context, s *Scope) context.Context
- type ActionRow
- type ActionsAnswer
- type CAList
- type CallError
- type Cataloguer
- type Compiled
- func (c *Compiled) Call(ctx context.Context, export string, input []byte, budget time.Duration) ([]byte, error)
- func (c *Compiled) Close(ctx context.Context) error
- func (c *Compiled) Describe(ctx context.Context, locale string) (*wire.Manifest, error)
- func (c *Compiled) HasExport(ctx context.Context, export string) (bool, error)
- type DryRunAnswer
- type DryRunEngine
- type DryRunInstalled
- type GatedRule
- type GitHubInput
- type Grants
- type HostFunc
- type InstallError
- type InstallInput
- type Installed
- type Item
- type JobOutput
- type JobQueue
- type LanguageRow
- type LogLine
- type Manifest
- type Need
- type Options
- type OutputSink
- type OverrideRow
- type PageFile
- type PageInfo
- type Permission
- type PermissionRow
- type Registry
- func (r *Registry) ActionsFor(ctx context.Context, isAdmin bool) (*ActionsAnswer, error)
- func (r *Registry) All() []*Installed
- func (r *Registry) AppLabel(name string) wire.Text
- func (r *Registry) ByID(id int64) (*Installed, bool)
- func (r *Registry) ByName(name string) (*Installed, bool)
- func (r *Registry) CACertPEM(ctx context.Context, tenantID int64) (string, error)
- func (r *Registry) CACertsPEM(ctx context.Context, tenantID int64) (string, error)
- func (r *Registry) CAs(ctx context.Context, tenantID int64) ([]CAList, error)
- func (r *Registry) CheckPIN(ctx context.Context, sh *model.Share, pin string) error
- func (r *Registry) Close(ctx context.Context)
- func (r *Registry) DecorateOps(ctx context.Context, rows []*ops.Op)
- func (r *Registry) Dir() string
- func (r *Registry) Engines() map[string]bool
- func (r *Registry) FetchGitHub(ctx context.Context, in GitHubInput) (*InstallInput, error)
- func (r *Registry) FetchURL(ctx context.Context, in URLInput) (*InstallInput, error)
- func (r *Registry) ImportCA(ctx context.Context, tenantID int64, certPEM, keyPEM []byte) (*signingKey, error)
- func (r *Registry) Install(ctx context.Context, in *InstallInput) (*Status, *DryRunAnswer, error)
- func (r *Registry) InstanceID() string
- func (r *Registry) LanguageRows(m *Manifest) []LanguageRow
- func (r *Registry) LinkOf(sh *model.Share) *db.AppLink
- func (r *Registry) Load(ctx context.Context) error
- func (r *Registry) LoadPage(ctx context.Context, token string) (*model.Share, *Installed, error)
- func (r *Registry) LockReason(l *model.AppPluginLock) (string, wire.Text)
- func (r *Registry) Locks(ctx context.Context, storageID int64) ([]*model.AppPluginLock, error)
- func (r *Registry) Logs(id int64, after int64) ([]LogLine, int64, error)
- func (r *Registry) LookupUsers(ctx context.Context, q string) ([]UserRow, error)
- func (r *Registry) MintUnlock(token string) string
- func (r *Registry) Overrides(ctx context.Context, id int64) ([]OverrideRow, error)
- func (r *Registry) PageAnchor(ctx context.Context, sh *model.Share) (int64, string)
- func (r *Registry) PageEvent(ctx context.Context, sh *model.Share, p *Installed, in wire.ViewEventInput, ...) (*wire.Surface, error)
- func (r *Registry) PageFile(sh *model.Share, ref string) (*PageFile, *os.File, error)
- func (r *Registry) PageInfo(sh *model.Share, p *Installed, unlocked bool) *PageInfo
- func (r *Registry) PutOverrides(ctx context.Context, id int64, rows []OverrideRow) error
- func (r *Registry) PutSettings(ctx context.Context, id int64, values map[string]string) error
- func (r *Registry) Remove(ctx context.Context, id int64) error
- func (r *Registry) RequiresSignature() bool
- func (r *Registry) ResolveAction(ctx context.Context, plugin, action string, isAdmin bool) (*Installed, *wire.Action, wire.Applies, error)
- func (r *Registry) RotateCA(ctx context.Context, tenantID int64) error
- func (r *Registry) RunDueSchedule(ctx context.Context, now time.Time) (int, error)
- func (r *Registry) RunPluginAction(ctx context.Context, op *ops.Op, live func(done, total int64)) error
- func (r *Registry) ScheduleOf(ctx context.Context, id int64) ([]*model.AppPluginScheduleItem, error)
- func (r *Registry) SetCatalogue(keys []string)
- func (r *Registry) SetEnabled(ctx context.Context, id int64, on bool) (*Status, error)
- func (r *Registry) SetHTTPTransport(t http.RoundTripper)
- func (r *Registry) SetHomeResolver(f func(ctx context.Context, u *model.User) string)
- func (r *Registry) SetJobQueue(q JobQueue)
- func (r *Registry) SetMailer(m mailSink)
- func (r *Registry) SetNotify(n notifySink)
- func (r *Registry) SetOrigins(fn func(ctx context.Context, storageID int64) string)
- func (r *Registry) SetOutputSink(s OutputSink)
- func (r *Registry) SetPublicURL(base string)
- func (r *Registry) SetStorageResolver(fn func(int64) (storage.Driver, error))
- func (r *Registry) SetUserScope(fn func(ctx context.Context, u *model.User) context.Context)
- func (r *Registry) SetVisibility(fn func(ctx context.Context, u *model.User, storageID int64, rel string) bool)
- func (r *Registry) Settings(ctx context.Context, id int64) (map[string]string, []wire.Field, error)
- func (r *Registry) StartScheduler(ctx context.Context)
- func (r *Registry) StartSweeper(ctx context.Context)
- func (r *Registry) StatusOf(p *Installed) *Status
- func (r *Registry) StorageName(ctx context.Context, id int64) string
- func (r *Registry) SweepPages(ctx context.Context) int
- func (r *Registry) SweepSchedule(ctx context.Context) int64
- func (r *Registry) Tick(ctx context.Context, p *Installed, now, windowEnd time.Time) (*wire.TickOutput, error)
- func (r *Registry) UILocale(code string) map[string]string
- func (r *Registry) UILocaleCodes() []string
- func (r *Registry) UILocaleList() []UILocaleInfo
- func (r *Registry) UIString(code, key string) (string, bool)
- func (r *Registry) Unlock(ctx context.Context, storageID int64, rel string) (bool, error)
- func (r *Registry) Upgrade(ctx context.Context, id int64, in *InstallInput) (*Status, *DryRunAnswer, error)
- func (r *Registry) VerifyUnlock(token, value string) bool
- func (r *Registry) ViewEvent(ctx context.Context, plugin, view string, storageID int64, rels []string, ...) (*wire.Surface, error)
- func (r *Registry) WakeSoon(ctx context.Context, pluginID int64, why string)
- type Runtime
- type Scope
- type Status
- type UILocaleInfo
- type URLInput
- type UserRow
- type ViewRow
Constants ¶
const ( CodePluginError = "plugin_error" // the guest set an error message CodePluginTrap = "plugin_trap" // the guest crashed (unreachable, OOB…) CodePluginOOM = "plugin_oom" // the guest hit its memory ceiling CodeTimeout = "timeout" // the call outlived its budget CodeRefused = "refused" // describe disagrees with the manifest CodeUnsupported = "unsupported" // this host cannot run plugins (arch) )
Outcome codes a guest call can end with. They are what the job row, the admin log and the user-facing message key on — never a wazero stack.
const ( KindApp = "app" KindLanguagePack = "language_pack" )
The two kinds of app, as Status.Kind and DryRunAnswer.Kind say them.
const ( DefaultMemoryPages = 1024 // 64 MiB MaxMemoryPages = 4096 // 256 MiB DefaultCallTimeout = 15 // seconds, views/pages/describe MaxCallTimeout = 60 DefaultJobTimeout = 300 // seconds, action_run MaxJobTimeout = 900 // MaxTickTimeout caps the hourly wake-up. It is deliberately tighter // than a screen's ceiling: a screen has a person waiting and 60 s is // defensible, while a wake-up runs unattended and one that hangs holds // up every other app's schedule behind it. MaxTickTimeout = 30 )
Limits the host imposes on what a manifest may ask for.
const ( StateRunning = "running" StateDisabled = "disabled" StateRefused = "refused" StateFailed = "failed" )
Plugin states.
const ( ErrCodeManifestInvalid = "manifest_invalid" ErrCodeSHA256Mismatch = "sha256_mismatch" ErrCodeSHA256Required = "sha256_required" ErrCodeSignatureRequired = "signature_required" ErrCodeSignatureInvalid = "signature_invalid" ErrCodePermissionsIncomplete = "permissions_incomplete" ErrCodePermissionsChanged = "permissions_changed" ErrCodeNameTaken = "name_taken" ErrCodeDescribeMismatch = "describe_mismatch" ErrCodeTooLarge = "too_large" ErrCodeDemo = "demo_refused" ErrCodeFetch = "fetch_failed" ErrCodeNotFound = "not_found" )
Install error codes.
const ( // FetchReasonBadRepo: the repository is not written as owner/name. FetchReasonBadRepo = "bad_repo" // FetchReasonManifestNotFound: no filex-app.json at any ref tried — a // repository that does not exist, is private, has no manifest at its // root, or a ref that does not exist. FetchReasonManifestNotFound = "manifest_not_found" // FetchReasonModuleNotFound: the manifest names a module address that // answers 404 — usually a release whose asset was never uploaded. FetchReasonModuleNotFound = "module_not_found" // FetchReasonUnreachable: no answer at all (DNS, network, TLS). FetchReasonUnreachable = "unreachable" // FetchReasonHTTPStatus: an answer, but not a success or a 404. FetchReasonHTTPStatus = "http_status" // FetchReasonBadURL: not an https address (plain http only for loopback). FetchReasonBadURL = "bad_url" // FetchReasonMissingURL: a URL install without its addresses — the // manifest always, the module unless the manifest is a language pack. FetchReasonMissingURL = "missing_url" // FetchReasonTooLarge: the answer was larger than the cap. FetchReasonTooLarge = "too_large" )
Why a fetch failed (InstallError.Reason on fetch_failed).
const ( // TickInterval is how often a scheduled app is woken. The wake-up is // re-armed to the next boundary of this interval, so an instance that // has been up for a week still wakes on the hour. TickInterval = time.Hour // MaxScheduleItems / MaxSchedulePaths are the wire contract's numbers, // not a second copy: the guest is told them in every TickInput and the // test kit checks an answer against them, so they are named once in // pkg/pluginkit/wire and enforced here. MaxScheduleItems = wire.ScheduleMaxItems MaxSchedulePaths = wire.ScheduleMaxPaths )
Bounds. Everything a wake-up can ask for is capped here; a plugin is told the two it has to plan around (MaxScheduleItems, MaxSchedulePaths) in its TickInput, so it can choose what to drop rather than having it chosen.
Variables ¶
var ( ErrPageNotFound = errors.New("page not found") ErrPageGone = errors.New("page is no longer available") )
Page errors the HTTP layer maps to status codes. The PIN-gate errors are internal/share's (ErrBadPIN / ErrLocked): there is one gate now.
var ErrUnsupportedArch = errors.New("wasmplugin: app plugins need an amd64 or arm64 host (wazero's interpreter is refused — it would take every core)")
ErrUnsupportedArch is returned by New on a CPU wazero has no compiler for.
var HostVersion = "dev"
HostVersion is what `describe` is told; filled by the server from its build.
var SurfaceNodeTypes = map[string]bool{ "text": true, "form": true, "steps": true, "list": true, "progress": true, "people-picker": true, "pin-input": true, "file-chooser": true, "pdf-fields": true, "signature-pad": true, "preview": true, "divider": true, "row": true, }
SurfaceNodeTypes is the v1 catalogue.
Functions ¶
func ArchSupported ¶
func ArchSupported() bool
ArchSupported reports whether wazero has a compiler for this CPU.
func CheckOpen ¶
CheckOpen validates a surface's `open` against the plugin that answered: the screen it names must be that plugin's own, and it may not name both. The PATH is checked by the handler, which is where a caller's permissions are known — a screen must not become a way to look at a file the person asking could not open themselves.
func DecodeInstallBody ¶
func DecodeInstallBody(b []byte) (map[string]json.RawMessage, error)
decodeJSONBody is a small helper for the handlers' JSON install bodies.
func EncodeJobText ¶ added in v0.46.0
EncodeJobText is t as a job column: one plain string when every language says the same (or only one was written), else every language, marked.
func ExposedFiles ¶
ExposedFiles lists the copies an app link exposed, straight off the row.
⚠ Deliberately a free function and not a Registry method: the no-JS page must be able to hand a visitor the document they were sent even on an instance where the plugin runtime is unavailable (an architecture without wasm, a plugin that failed to load). The copies are already on disk and belong to the share, not to the running app.
func JobText ¶ added in v0.46.0
JobText reads a job column in lang (wire.Text.Get: the language, its base language, English, any). A plain column — every row written before EncodeJobText, a progress line, the host's own words — comes back as it is.
func JobTimeout ¶
JobTimeout is the clamped budget for one action_run of an action.
func LocalizeNote ¶
LocalizeNote reads a schedule row's note in `lang`: a note written by wakeNote is composed in that language; a {lang: …} object (a row written before the record existed) is read the same way; anything else (an error) is returned as it is.
func Matches ¶
Matches reports whether an Applies rule accepts a selection described by (kind, ext, mime) per item. It is the one implementation the server uses for every check; the explorer mirrors it client-side for the menu.
func NextTickBoundary ¶
NextTickBoundary is when the wake-up after now falls: the next boundary of TickInterval, in UTC. Boundaries rather than "an hour from whenever it last ran" so a restart at 03:37 does not move every later wake-up to :37.
func OutputOverride ¶
OutputOverride reads the per-job output stored in params, or nil.
func ParseCatalogue ¶
ParseCatalogue reads the embedded catalogue file (a flat key → English object — the exact shape of one `ui_locales` language) into its keys.
func PersonalStateKeys ¶
PersonalStateKeys is the view of a file's state keys (`<plugin>:<key>`) that ONE person gets: a personal key `<key>@<id>` (wire.PersonalState) is that person's own and reads `<key>@me`; anybody else's is left out, so a listing never says who else has something to do on the file. Other keys pass as they are. The listing (app badges) and the run check (authorise) both read the state through this, so the menu and the server agree.
func SanitizeSurface ¶
SanitizeSurface prunes unknown node types, caps counts and depth.
func SetOutputOverride ¶
SetOutputOverride stores o under the override key.
func TickTimeout ¶
TickTimeout is the clamped budget for one `tick` call: whatever the manifest asked for as a call timeout, never more than MaxTickTimeout.
func ValidOutputMode ¶
ValidOutputMode is the closed set an Output.Mode may take. `folder` is a per-job choice only (a manifest declares `sibling` + `elsewhere`).
func ValidScheduleKey ¶
ValidScheduleKey reports whether an app may use key for a scheduled item.
func WithActorIP ¶
WithActorIP carries the address a signed-in person's request came from into a view call, where it becomes `context.actor.ip` (wire.Actor.IP). The handler knows the request (and the trusted-proxy rule that reads it); the registry does not, and should not have to.
Types ¶
type ActionRow ¶
type ActionRow struct {
Plugin string `json:"plugin"`
ID string `json:"id"`
Key string `json:"key"`
Label wire.Text `json:"label"`
Icon string `json:"icon,omitempty"`
Applies wire.Applies `json:"applies"`
View string `json:"view,omitempty"`
// ViewPlacement is the declared placement of View (modal | page), so
// the menu knows whether to open a dialog or a new tab.
ViewPlacement string `json:"view_placement,omitempty"`
Confirm wire.Text `json:"confirm,omitempty"`
MinRole string `json:"min_role,omitempty"`
Danger bool `json:"danger,omitempty"`
OutputMode string `json:"output_mode"`
// OutputElsewhere: the result may go into a folder the person chooses
// (manifest output.elsewhere), so the action is offered on a read-only
// storage too — its screen asks where the result should go.
OutputElsewhere bool `json:"output_elsewhere,omitempty"`
// Gated is what the action WOULD also be offered on once a requirement
// the server lacks is met — the extensions LibreOffice would add to the
// signing app's "Sign…", say. Sent to administrators only: the owner's
// rule for anything that depends on how the server is set up is "greyed
// with the reason for an administrator, not shown to anybody else", so
// the explorer draws each as a disabled row that says what is missing.
Gated []GatedRule `json:"gated,omitempty"`
}
ActionRow is one menu entry as GET /api/files/plugins/actions returns it.
type ActionsAnswer ¶
ActionsAnswer is the whole list for one caller.
type CAList ¶
type CAList struct {
ID string `json:"id"`
Subject string `json:"subject"`
Issuer string `json:"issuer"`
Fingerprint string `json:"fingerprint"`
Imported bool `json:"imported"`
NotBefore time.Time `json:"not_before"`
NotAfter time.Time `json:"not_after"`
CreatedAt time.Time `json:"created_at"`
RetiredAt *time.Time `json:"retired_at,omitempty"`
Live bool `json:"live"`
}
CAList describes one signing authority for the admin surface.
type CallError ¶
type CallError struct {
Code string
Export string
Message string // safe for a user; the guest's own words when it set any
Cause error // the runtime's error, for the plugin log only
}
CallError is a failed guest call, classified.
type Cataloguer ¶
type Cataloguer interface {
Catalogue(ctx context.Context, storageID int64, rel string) (*model.Node, error)
}
Cataloguer records in the catalogue a file the STORAGE has and the catalogue has not seen yet, with the same bookkeeping a write through filex gets (node row, parent rows, index, thumbnail), and answers its node.
The output sink implements it (handlers.AppPlugins, through protocolsync); it is a separate interface so a sink that does not — every test double of OutputSink — leaves share_create exactly as strict as it was.
type Compiled ¶
type Compiled struct {
Name string
Manifest *Manifest
SHA256 string
// contains filtered or unexported fields
}
Compiled is one installed plugin's module, compiled once and instantiated per call.
func (*Compiled) Call ¶
func (c *Compiled) Call(ctx context.Context, export string, input []byte, budget time.Duration) ([]byte, error)
Call runs one export on a fresh instance with a wall-clock budget. The returned bytes are the guest's output. Errors are *CallError.
ctx may carry a call scope (WithScope) that host functions read to decide what this particular call is allowed to touch; the guest never sees it.
func (*Compiled) Describe ¶
Describe calls the guest's describe export and checks that it agrees with the installed manifest on the things a grant depends on. The file on disk is the operator's intent; the guest's answer is the proof it is the same program.
type DryRunAnswer ¶
type DryRunAnswer struct {
Manifest *wire.Manifest `json:"manifest"`
Permissions []PermissionRow `json:"permissions"`
// WasmSHA256 / WasmBytes describe the module; both empty for a language
// pack, which has none (ManifestSHA256 is what was verified instead).
WasmSHA256 string `json:"wasm_sha256"`
WasmBytes int64 `json:"wasm_bytes"`
Signed bool `json:"signed"`
// Kind: KindApp | KindLanguagePack. The review says which BEFORE the
// install, so an administrator is not asked to trust a module that is
// not there, or surprised by one that is.
Kind string `json:"kind"`
ManifestSHA256 string `json:"manifest_sha256,omitempty"`
// Languages: what the app would add to filex itself — coverage of the
// current catalogue, and whether each is written right to left.
Languages []LanguageRow `json:"languages,omitempty"`
// Installed is the app of the same name already on this server (an
// install cannot proceed; an upgrade of it can). Nil on an upgrade's own
// dry run, and when nothing of that name is installed.
Installed *DryRunInstalled `json:"installed,omitempty"`
// EnginesMissing are the engines the manifest asks for that this server
// does not have: the app installs, and whatever needs them will not work.
EnginesMissing []DryRunEngine `json:"engines_missing,omitempty"`
}
DryRunAnswer is what a dry run returns for the review step.
⚠ Installed and EnginesMissing are said HERE, at the review, because the dry run already knows them. Before, reinstalling an app that was already there passed the review and only "Install" answered "an app with this name is already installed", and an app needing an engine this server lacks was reviewed as if it would work (release-candidate sweep, 2026-09-21).
type DryRunEngine ¶
DryRunEngine is one engine by its id and by the name a person reads.
type DryRunInstalled ¶
DryRunInstalled names the installed app a new install would collide with.
type GitHubInput ¶
GitHubInput is the JSON body of a GitHub install.
type Grants ¶
type Grants map[Permission]bool
Grants is the set an administrator approved.
func NewGrants ¶
func NewGrants(perms []Permission) Grants
NewGrants builds a set from validated permissions.
func (Grants) AllowedHosts ¶
AllowedHosts lists the http:* grants as Extism allowed_hosts patterns.
func (Grants) HasHost ¶
HasHost answers whether an outbound HTTP request to host is allowed: an exact "http:host" grant, or a "http:*.domain" grant covering a subdomain.
func (Grants) Missing ¶
func (g Grants) Missing(want []Permission) []Permission
Missing returns the permissions in want that are not in g — what an upgrade must ask the administrator to approve again.
type HostFunc ¶
type HostFunc = extism.HostFunction
HostFunc is a host function the runtime links into every instance. The registry builds them (hostfn.go); tests pass a few of their own.
type InstallError ¶
type InstallError struct {
Code string
Message string
Missing []string
Reason string // a FetchReason* for fetch_failed, else ""
Where string // the repository or the URL that was being fetched
Refs []string // the refs a repository install tried
Status int // the HTTP status that came back, when one did
}
InstallError is a failure caused by what the caller supplied. Code is what the HTTP layer answers; Missing carries the permission diff for permissions_incomplete / permissions_changed.
Reason, Where, Refs and Status narrow a fetch_failed, so the install wizard can write a sentence in the reader's language that says WHAT to check. ⚠ The message alone used to be the answer: "filex-app.json not found in BRF-Tech/yok-boyle-bir-depo: http 404 from raw.githubusercontent.com", English, in the Turkish wizard (release-candidate sweep, 2026-09-21).
func (*InstallError) Error ¶
func (e *InstallError) Error() string
type InstallInput ¶
type InstallInput struct {
Manifest []byte
// Wasm is the module; Reader is consumed, capped at MaxWasmBytes.
Wasm io.Reader
// SHA256 (lower hex) the module must match; "" skips (upload only).
SHA256 string
Signature string
Source string
SourceURL string
// Granted is the permission list the admin approved. It must equal the
// manifest's set exactly.
Granted []string
DryRun bool
Lang string
}
InstallInput is one install or upgrade request after the HTTP layer has gathered the pieces.
type Installed ¶
type Installed struct {
Row *model.AppPlugin
Manifest *Manifest
Grants Grants
// Perms is the grant as a sorted list, for answers.
Perms []Permission
// contains filtered or unexported fields
}
Installed is one plugin's live entry.
type Item ¶
type Item struct {
Kind string // file | dir
Ext string // lower-case, no dot
Mime string
// State is the set of state keys the plugin under consideration keeps
// on this item (bare keys). Filled per plugin by the caller.
State []string
}
Item is what Matches sees of a selected entry.
type JobOutput ¶
type JobOutput struct {
Path string `json:"path"`
}
JobOutput is one committed output, as the ops row reports it.
type JobQueue ¶
type JobQueue interface {
SubmitTo(ctx context.Context, kind string, storageID, destStorageID int64, sources []string, dest string) (*ops.Op, error)
}
JobQueue is the ops queue, as the scheduler needs it: a way to hand an app_plugin_jobs row to the same worker a person's job goes through. *ops.Service satisfies it; the server injects it with SetJobQueue.
Without it a due item fails loudly (status `failed`, the reason on the row) rather than disappearing.
type LanguageRow ¶
type LanguageRow struct {
Code string `json:"code"`
// Keys the pack carries for this language (empty values not counted).
Keys int `json:"keys"`
// Translated is how many of those the current catalogue has; Unknown is
// the rest — keys filex no longer has, or never had (typos). Both are
// only meaningful when Total > 0.
Translated int `json:"translated"`
Unknown int `json:"unknown"`
// Total is the size of the current catalogue; 0 means this binary was
// built without one (a development build), and coverage is unknown.
Total int `json:"total"`
// Percent is floor(100 × Translated / Total): 100 only when nothing is
// missing, so "100 %" is never a rounding of "almost".
Percent int `json:"percent"`
// RTL: a right-to-left language (wire.IsRTL) — the interface is laid out
// right to left in it.
RTL bool `json:"rtl"`
}
LanguageRow is one language an app adds, as the Apps screens show it.
⚠ Coverage is measured against the CURRENT catalogue — the one this very binary's interface draws from — not against whatever the pack's author had when they wrote it. A pack written for v0.43 and run on v0.45 says how much of v0.45 it covers, and every string it lacks falls back to English.
type LogLine ¶
type LogLine struct {
Seq int64 `json:"seq"`
TS time.Time `json:"ts"`
Level string `json:"level"`
Msg string `json:"msg"`
}
LogLine is one entry of a plugin's ring buffer: what the guest logged through pdk.Log plus the host's own warnings about it (refused calls, failed loads). The admin panel polls it with ?after=<seq>.
type Manifest ¶
type Manifest struct {
wire.Manifest
// Perms is Permissions parsed and validated.
Perms []Permission
}
Manifest is a validated filex-app.json.
func ParseManifest ¶
ParseManifest decodes and validates a manifest document.
func (*Manifest) CallTimeout ¶
CallTimeout is the clamped per-call budget for describe/view/page calls.
func (*Manifest) MemoryPages ¶
MemoryPages is the clamped wasm memory ceiling.
func (*Manifest) WritesElsewhere ¶
WritesElsewhere reports whether any action may put its result into a folder the person chooses (output.elsewhere) — what makes the host tell the app where that person's results go by default (CallContext.Home).
type Need ¶
Need is a requirement the server does not meet: `kind` says which sort ("engine" today), `id` the machine's word, `name` the one a person reads.
type Options ¶
type Options struct {
Store db.Store
// a share (migration 00046), so it goes through the same service every
// other link does and inherits the instance's expiry ceiling, the PIN
// gate and the administrator's revoke. nil refuses share_create.
Share *share.Service
// Dir is where modules live; the compilation cache and the call spool
// sit next to it.
Dir string
// SecretKey seals secret settings (secretbox). Empty → secret fields are
// refused at PutSettings rather than stored in the clear.
SecretKey string
// TrustedKeys (hex/base64 ed25519) make a detached signature mandatory.
TrustedKeys []string
// Demo refuses every install/upgrade (the admin account is public).
Demo bool
// HTTP fetches manifests and modules for URL/GitHub installs.
HTTP *http.Client
Log *slog.Logger
// StorageResolver opens the driver a job reads from and writes to.
StorageResolver func(int64) (storage.Driver, error)
// Limits (bytes). Zero → defaults below.
MaxWasmBytes int64
MaxInputBytes int64
MaxOutputBytes int64
// PerPluginJobs bounds concurrent action_run calls per plugin (default 2).
PerPluginJobs int
}
Options configures the registry.
type OutputSink ¶
type OutputSink interface {
// CommitSibling writes a NEW file next to the first input, taking a
// unique name when the preferred one is taken. Returns the final path.
CommitSibling(ctx context.Context, storageID int64, dir, name string, r io.Reader, size int64, actor *int64) (string, error)
// CommitVersion overwrites rel in place (the previous bytes become a
// version when versioning is on).
CommitVersion(ctx context.Context, storageID int64, rel string, r io.Reader, size int64, actor *int64) error
}
OutputSink lands a finished output on a storage with filex's own bookkeeping (kind guard, version snapshot on overwrite, node row, index, thumbnail, write hook, change frame). Implemented by the HTTP layer, which owns those services; the registry only decides WHAT to write WHERE.
type OverrideRow ¶
type OverrideRow struct {
ID string `json:"id"`
Enabled bool `json:"enabled"`
AdminOnly bool `json:"admin_only"`
Applies *wire.Applies `json:"applies"`
}
OverrideRow is the admin API shape of one action override. Applies is a WHOLE rule both ways — the editor shows a list and sends a list — while the row stores only what changed against the manifest (applies_override.go says why). Nil = as the manifest says.
type PageFile ¶
type PageFile struct {
Ref string `json:"ref"`
Name string `json:"name"`
File string `json:"file"`
Size int64 `json:"size"`
Mime string `json:"mime,omitempty"`
}
PageFile is one exposed copy, as files_json stores it.
⚠ `File` is a BASENAME inside the share's own directory, not an absolute path. v2 stored the absolute path, so moving or restoring the data directory silently broke every exposed copy while the row looked healthy.
type PageInfo ¶
type PageInfo struct {
Plugin string `json:"plugin"`
Page string `json:"page"`
Title wire.Text `json:"title"`
Subject string `json:"subject,omitempty"`
RequiresPIN bool `json:"requires_pin"`
Unlocked bool `json:"unlocked"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Files []wire.FileRef `json:"files"`
}
PageInfo describes an app link without opening it.
type Permission ¶
type Permission string
Permission is one capability a plugin asks for in its manifest and an administrator grants at install. The set is CLOSED: a manifest naming a permission this host does not know is refused at install, because a grant the admin cannot read is not a grant.
Two shapes: bare names ("files:read") and parameterised ones ("http:api.example.com", "engines:ffmpeg"). The parameter is part of the permission — granting "http:a.example.com" says nothing about b.example.com.
const ( PermFilesRead Permission = "files:read" PermFilesWrite Permission = "files:write" PermFilesLock Permission = "files:lock" PermSign Permission = "sign" PermMailSend Permission = "mail:send" PermNotifySend Permission = "notify:send" PermUsersLookup Permission = "users:lookup" PermSettings Permission = "settings" PermState Permission = "state" PermPublicPages Permission = "public_pages" // PermSchedule is the one permission that makes an app run when nobody // asked it to: it is woken once an hour and runs the work it scheduled // at the minute it named. An app without it is never woken, and there // is no other way in — see schedule.go. PermSchedule Permission = "schedule" )
Bare permissions.
func ParsePermission ¶
func ParsePermission(s string) (Permission, error)
ParsePermission validates one manifest entry.
func (Permission) Label ¶
func (p Permission) Label(lang string) string
Label is the plain-language meaning shown at install, in lang — any language the server catalogue speaks, a language pack's included (keys `server.perm.*`, internal/srvtext). It serves the API (`GET /api/admin/ app-plugins/{id}` → permission review) and the CLI.
⚠ This is what an administrator reads before trusting an app with their files. It was English or Turkish — a Spanish administrator read the one screen that asks for consent in English, around a Spanish wizard. A key a pack lacks falls back to English, never to the permission id.
type PermissionRow ¶
type PermissionRow struct {
ID string `json:"id"`
Label string `json:"label"`
Reason wire.Text `json:"reason,omitempty"`
}
PermissionRow is one line of the install-time review.
func PermissionRows ¶
func PermissionRows(m *Manifest, lang string) []PermissionRow
PermissionRows renders the review for a manifest in lang.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is the installed set plus the runtime.
func (*Registry) ActionsFor ¶
ActionsFor lists what the caller may see: running plugins, enabled actions, admin-only ones only for admins.
func (*Registry) All ¶
All returns every entry, by name. (The languages the running apps add to filex itself — UILocaleList / UILocale — live in langpack.go.)
func (*Registry) AppLabel ¶
AppLabel is an installed app's own label, in every language its manifest wrote it in ({"en": "e-Signature", "tr": "e-İmza"}), or nil when no app of that name is installed here.
⚠ A lock, an action row and a job all carry the app's manifest NAME (`sign`), because that is what addresses it; a person reading "sign locked this file" is reading an identifier the rest of the product never shows them — every list, the install review and the app's own page say "e-Signature" (v0.43.0). So the words a person reads resolve the name to this, and fall back to the name only where the app is gone.
func (*Registry) CACertPEM ¶
CACertPEM returns the tenant CA certificate (creating the CA on first use).
func (*Registry) CACertsPEM ¶
CACertsPEM returns EVERY certificate this tenant has ever signed with — the live CA first, then the retired ones, newest first — as one PEM bundle. This is the root set a verifier needs: a signature made two CAs ago must still check out, so nothing here is ever deleted.
func (*Registry) CAs ¶
CAs lists the tenant's signing authorities, live and retired, for the admin panel and the verify screen: who signed, with what, until when.
func (*Registry) CheckPIN ¶
CheckPIN verifies a PIN with the strike counter. A one-line forward: the gate itself is internal/share/pin.go, so /s/, /d/ and an app page cannot drift apart — which is exactly what v2 did (only the app page had a lock).
func (*Registry) DecorateOps ¶
DecorateOps fills the plugin fields of ops rows that are plugin jobs. DecorateOps fills the plugin columns of ops rows. A read failure is logged rather than swallowed: the row still renders, but as a nameless "plugin-action" with no message, which is how two different lost-update bugs stayed invisible.
func (*Registry) FetchGitHub ¶
func (r *Registry) FetchGitHub(ctx context.Context, in GitHubInput) (*InstallInput, error)
FetchGitHub resolves a repository + ref into an InstallInput (manifest and module fetched, sha256 taken from the manifest unless overridden).
func (*Registry) ImportCA ¶
func (r *Registry) ImportCA(ctx context.Context, tenantID int64, certPEM, keyPEM []byte) (*signingKey, error)
ImportCA takes over signing with an authority the operator already has — their own PKI, or a certificate they trust everywhere else. The current CA is retired, not deleted, so everything it signed keeps verifying.
certPEM may be a bundle: the signing certificate first, its issuers after it. keyPEM is that certificate's private key, in PKCS#8, PKCS#1 or SEC1 form, NOT encrypted — a .p12/.pfx is converted first (`openssl pkcs12 -in ca.p12 -nodes -out ca.pem`), because the encrypted forms in the wild are more varied than any one library reads.
func (*Registry) Install ¶
func (r *Registry) Install(ctx context.Context, in *InstallInput) (*Status, *DryRunAnswer, error)
Install stages, reviews and installs a plugin.
func (*Registry) InstanceID ¶
InstanceID is what this process writes into claimed_by.
func (*Registry) LanguageRows ¶
func (r *Registry) LanguageRows(m *Manifest) []LanguageRow
LanguageRows describes every language m adds, with its coverage. Nil-safe on the receiver (no catalogue → coverage unknown).
func (*Registry) LinkOf ¶
LinkOf says what an app's link IS, for a list of links (My shares, the admin's Shares): the purpose the app gave the link when it opened it, else the one its page declares, and the app's home view the row opens. Nil for an ordinary share, for an app that is gone, and for a link with no purpose — the row then lists as a plain share.
⚠ Page-less links too (an app's plain share of a file: PluginID set, no PageID). The finished document's delivery link listed as a share somebody made by hand, beside signing links marked as what they are.
func (*Registry) Load ¶
Load reads every row and compiles the enabled ones. A row that fails to load is kept (state failed/refused) so the admin sees why; it never stops the server.
func (*Registry) LoadPage ¶
LoadPage resolves a token to the live app share behind it (ErrPageNotFound / ErrPageGone).
⚠ A token that names an ordinary share is ErrPageNotFound here, not an error about kinds: this is an unauthenticated lookup and "wrong sort of link" is a fact about somebody else's data.
func (*Registry) LockReason ¶
LockReason is a lock's reason as words: the plain text (English, for a consumer with no reader — a log, a protocol server) and, for a reason the app named by message key, the same words in every language the app wrote them in. A plain reason has no Text; a message whose app or key is gone has no words at all (never the raw key).
func (*Registry) LookupUsers ¶
LookupUsers is the one directory search behind both the host function and the HTTP route. ctx must already carry the caller's tenant scope (the store is the tenant-scoped wrapper, which confines ListUsers by that scope).
func (*Registry) MintUnlock ¶
MintUnlock / VerifyUnlock forward to the same one implementation.
func (*Registry) Overrides ¶
Overrides returns one row per manifest action, merged with stored changes.
func (*Registry) PageAnchor ¶
PageAnchor reports the storage and storage-relative path the link is about — what per-file state and the follow-up job hang on. Zero/"" when the node has gone.
func (*Registry) PageEvent ¶
func (r *Registry) PageEvent(ctx context.Context, sh *model.Share, p *Installed, in wire.ViewEventInput, visitorIP string) (*wire.Surface, error)
PageEvent runs the link's page_event export for an unlocked visitor. "open" spends one visit off the share's own counter. A surface carrying a job is returned as-is; the HTTP layer queues it as the share's creator.
func (*Registry) PutOverrides ¶
PutOverrides stores the rows (unknown and hidden action ids dropped, applies validated).
func (*Registry) PutSettings ¶
PutSettings stores values. A secret field sent as "***" keeps its current value; other secret values are sealed. Unknown keys are dropped.
func (*Registry) RequiresSignature ¶
RequiresSignature reports whether installs must carry a signature.
func (*Registry) ResolveAction ¶
func (r *Registry) ResolveAction(ctx context.Context, plugin, action string, isAdmin bool) (*Installed, *wire.Action, wire.Applies, error)
ResolveAction finds a running plugin's enabled action for a run request. isAdmin gates admin-only actions. The error is a *CallError with CodeUnsupported (not running / disabled) or CodeRefused (admin only).
func (*Registry) RotateCA ¶
RotateCA retires the tenant CA; the next signature creates a fresh one. Leaves issued by the old CA stay verifiable through its certificate, which is why retiring never deletes.
func (*Registry) RunDueSchedule ¶
RunDueSchedule performs ONE pass: every row due at or before now, claimed and run. Exported so a test — and, later, an administrator's "run it now" — can drive the schedule without the loop.
A plugin that traps, panics or hangs takes down its own row and nothing else: each row runs behind a recover, and the call itself is bounded by the wasm runtime's own per-call budget.
func (*Registry) RunPluginAction ¶
func (r *Registry) RunPluginAction(ctx context.Context, op *ops.Op, live func(done, total int64)) error
RunPluginAction is what the ops worker calls for an OpPluginAction row. op.Dest carries the job id. Progress is fed to live so the tray can draw a bar; the job row records status, message and outputs.
func (*Registry) ScheduleOf ¶
func (r *Registry) ScheduleOf(ctx context.Context, id int64) ([]*model.AppPluginScheduleItem, error)
ScheduleOf returns one app's schedule rows, soonest first — what an admin screen would draw and what a test reads back.
func (*Registry) SetCatalogue ¶
SetCatalogue gives the registry the current string catalogue — every key of the explorer's and the admin panel's English tables — so the Apps screens can say how much of it a language covers.
⚠ Called with the keys of `admin/i18n/filex-catalogue-en.json`, which the web build writes (web/vite.config.ts → scripts/lib/i18n-catalogue.mjs) and the binary embeds. A binary built without the web build has none, and coverage then reads "unknown" rather than a made-up percentage.
func (*Registry) SetEnabled ¶
SetEnabled flips a plugin on or off, loading or unloading it.
func (*Registry) SetHTTPTransport ¶
func (r *Registry) SetHTTPTransport(t http.RoundTripper)
SetHTTPTransport replaces the transport http_request uses (tests).
func (*Registry) SetHomeResolver ¶
SetHomeResolver wires the answer to "where do this person's results go when not beside their source" (CallContext.Home) — the HTTP layer, which knows storages, tenants and the ACL.
func (*Registry) SetJobQueue ¶
SetJobQueue wires the queue scheduled work is handed to.
func (*Registry) SetMailer ¶
func (r *Registry) SetMailer(m mailSink)
SetMailer wires the mailer (nil → mail_send unavailable).
func (*Registry) SetNotify ¶
func (r *Registry) SetNotify(n notifySink)
SetNotify wires the notification service (nil → notify_send unavailable).
func (*Registry) SetOrigins ¶
SetOrigins records how an absolute link's origin is resolved when NO request is in scope — a plugin job runs on the queue, long after the browser left. The tenant then comes from the data (the share's storage), never from a guess: internal/tenanturl.Resolver.ForStorage is what is wired here. Without it a customer's signature request would carry the OPERATOR's hostname.
func (*Registry) SetOutputSink ¶
func (r *Registry) SetOutputSink(s OutputSink)
SetOutputSink wires the committer that lands job outputs on a storage.
func (*Registry) SetPublicURL ¶
SetPublicURL records the base the visitor link is built on. It is the fallback; SetOrigins is what a multi-tenant install answers with.
func (*Registry) SetStorageResolver ¶
SetStorageResolver wires (or replaces) the driver resolver jobs read through; the server builds it after the registry.
func (*Registry) SetUserScope ¶
SetUserScope wires the function that puts the acting user's tenant scope on a job context, so users_lookup from a queued job sees the same directory the user's own requests see. Nil = single-tenant, unscoped.
func (*Registry) SetVisibility ¶
func (r *Registry) SetVisibility(fn func(ctx context.Context, u *model.User, storageID int64, rel string) bool)
SetVisibility wires the check state_list runs on every row: a plugin may keep state on a file the person asking has no right to see, and a listing is not a way around the ACL. Nil = everything passes.
func (*Registry) StartScheduler ¶
StartScheduler runs the schedule until ctx ends. Call it once at boot, after the job queue is wired.
It does not poll on a fixed beat: after each pass it sleeps until the next row is actually due, capped at schedulePollCeiling so a row another process wrote is picked up too. That is what makes "to the minute" true — an item due at 03:00:00 runs at 03:00:00, not at the next tick of a one-minute timer.
func (*Registry) StartSweeper ¶
StartSweeper runs SweepPages every hour until ctx ends.
func (*Registry) StatusOf ¶
StatusOf builds the list row. ⚠ Nil-safe receiver: the wire-fixture test calls it on a nil registry, which then reports languages without coverage.
func (*Registry) StorageName ¶
StorageName is a storage's name, or "" when it is gone (or unreadable) — what the panel prints beside a lock instead of the storage's id.
func (*Registry) SweepPages ¶
SweepPages removes the exposed copies of links that are gone. Called hourly by the server; safe to call any time.
⚠ It walks the DIRECTORIES rather than querying for dead rows, because a directory can outlive its row: DeleteShare (the administrator's hard delete) leaves nothing to join against, and those copies would then sit on disk until somebody noticed. A directory is removed when its share is gone, or when the share has been dead for longer than the grace period.
func (*Registry) SweepSchedule ¶
SweepSchedule drops finished rows whose due time is past the retention window. Called once an hour from the schedule loop.
It measures on due_at rather than updated_at on purpose: due_at is always written from Go and updated_at from the engine's CURRENT_TIMESTAMP, and comparing a Go time against an engine-formatted column is how a sweep silently stops matching anything.
func (*Registry) Tick ¶
func (r *Registry) Tick(ctx context.Context, p *Installed, now, windowEnd time.Time) (*wire.TickOutput, error)
Tick wakes one plugin and returns what it wants done. Exported so a test (and a future "run it now" button) can drive one wake-up on its own.
The call gets a READ-ONLY scope with no storage and no actor: the same shape a screen gets. It may read settings and its own state, look people up, notify, mail and make granted HTTP calls; it may not write files, write state, take locks or sign. Those belong in the actions it schedules, which run as real jobs with a real scope.
func (*Registry) UILocale ¶
UILocale is one language's strings, merged over every running app that ships it; nil when none does.
⚠ First app (by name) wins a contested key — arbitrary, but stable across restarts, which is what matters to somebody reading the screen. ⚠ An empty value is left out rather than served: an app that has not translated a string yet must fall back to English, not blank it.
func (*Registry) UILocaleCodes ¶
UILocaleCodes lists the languages the running apps add, sorted — the tags the server's catalogue may answer in besides English and Turkish.
func (*Registry) UILocaleList ¶
func (r *Registry) UILocaleList() []UILocaleInfo
UILocaleList is every language the running apps add, by tag.
func (*Registry) UIString ¶
UIString is ONE key of one language, under the same rules as UILocale (running apps only, first app by name wins, an empty value is no value) — without building the whole ~3 000-key map. It is how the server's own catalogue (internal/srvtext) reads a pack: a mail or a public page asks for a few dozen keys, and UILocale would merge every string of every pack for each of them.
func (*Registry) Unlock ¶
Unlock lifts a lock by force — the administrator's override for a plugin that will never come back to lift it. Returns false when nothing was locked there.
func (*Registry) Upgrade ¶
func (r *Registry) Upgrade(ctx context.Context, id int64, in *InstallInput) (*Status, *DryRunAnswer, error)
Upgrade replaces the module + manifest of an installed plugin. A manifest that asks for a permission not yet granted answers permissions_changed until the request grants it; a narrower manifest simply narrows the grant.
func (*Registry) VerifyUnlock ¶
VerifyUnlock reports whether an unlock cookie belongs to this link.
func (*Registry) ViewEvent ¶
func (r *Registry) ViewEvent(ctx context.Context, plugin, view string, storageID int64, rels []string, actor *model.User, locale string, in wire.ViewEventInput) (*wire.Surface, error)
ViewEvent runs one view event and returns the sanitised surface.
func (*Registry) WakeSoon ¶
WakeSoon brings an app's hourly wake-up forward to a few seconds from now.
It is how the host tells an app that something it cannot see happened to one of its own things — today, a PERSON ended one of its links from the Shares screen (handlers: appLinkEnded). ⚠⚠ It carries no event and no payload, on purpose: the app is not told WHAT happened, it is woken, and its `tick` asks the host about its own links (`share_state` → the link's facts) exactly as it does every hour. One mechanism — the wake-up — and one way of learning — asking — rather than a revoke callback living beside them with its own delivery, retry and ordering rules. Without the nudge the same wake-up still notices within the hour; the nudge only makes it prompt.
A no-op for an app without the `schedule` grant, a stopped app and a demo instance — each of those is simply not woken, which is what they already were.
type Runtime ¶
type Runtime struct {
// contains filtered or unexported fields
}
Runtime owns the compilation cache and the per-host settings.
func NewRuntime ¶
New prepares a runtime. cacheDir holds compiled modules; "" disables the on-disk cache (tests).
func (*Runtime) Compile ¶
func (r *Runtime) Compile(ctx context.Context, wasmPath, sha256Hex string, m *Manifest, fns []HostFunc, logger func(level, msg string)) (*Compiled, error)
Compile validates the module against sha256 (lower-hex; "" skips the check) and compiles it with the given host functions.
type Scope ¶
type Scope struct {
// contains filtered or unexported fields
}
Scope is one call's ticket. Build with newScope; close with Close.
func (*Scope) Close ¶
func (s *Scope) Close()
Close shuts every open handle and removes the spool.
⚠ It also abandons any share this call PROMISED and did not keep (a job that failed, an output the plugin never named). Nothing has to be undone in the database — a promise is the absence of a row — but the copies staged for it are removed here, on every exit path, rather than left for the sweeper.
type Status ¶
type Status struct {
ID int64 `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
Label wire.Text `json:"label"`
Description wire.Text `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
Homepage string `json:"homepage,omitempty"`
Enabled bool `json:"enabled"`
State string `json:"state"`
StateError string `json:"state_error,omitempty"`
Source string `json:"source"`
SourceURL string `json:"source_url,omitempty"`
SHA256 string `json:"sha256"`
Signed bool `json:"signed"`
Permissions []string `json:"permissions"`
// Scheduled says this app is woken once an hour and runs work of its
// own choosing — the one thing on this row that happens with nobody
// present, so the list can mark it.
Scheduled bool `json:"scheduled"`
Actions int `json:"actions"`
Views int `json:"views"`
PublicPages int `json:"public_pages"`
// Kind is KindApp or KindLanguagePack (langpack.go). A language pack has
// no module, so `sha256` is the hash of its MANIFEST — the thing that was
// verified and, on an instance that requires signatures, signed.
Kind string `json:"kind"`
// Languages are the languages this app adds to filex itself, each with
// how much of the CURRENT catalogue it covers. Absent when it adds none.
Languages []LanguageRow `json:"languages,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Status is one plugin as the admin list shows it.
type UILocaleInfo ¶
type UILocaleInfo struct {
Code string `json:"code"`
// Source is always "plugin" here; the field is the client's
// PublicLocaleOption, which also describes the built-in languages.
Source string `json:"source"`
// Plugin names the app that brought it (the first, by name, when several
// do) — shown beside the language so nobody wonders where it came from.
Plugin string `json:"plugin"`
RTL bool `json:"rtl,omitempty"`
}
UILocaleInfo is one language the running apps add, as GET /api/public/branding lists it. ⚠ No strings: those are fetched for the one language a visitor actually picks (UILocale, GET /api/public/ui-locales/ {code}). A complete language is ~150 KB of JSON, and the branding answer is read by every public page and every panel start.
type URLInput ¶
type URLInput struct {
URL string `json:"url"`
ManifestURL string `json:"manifest_url"`
SHA256 string `json:"sha256"`
Signature string `json:"signature"`
}
URLInput is the JSON body of a URL install.
type UserRow ¶
type UserRow struct {
// UserID, not `id`: the row doubles as a people-picker VALUE, whose
// shape the surface contract fixes as {user_id, email, name}.
UserID int64 `json:"user_id"`
Email string `json:"email"`
Name string `json:"name,omitempty"`
}
UserRow is what users_lookup and GET /api/files/plugins/users return: the directory entry a picker needs and nothing else (no role, no flags).
type ViewRow ¶
type ViewRow struct {
Plugin string `json:"plugin"`
ID string `json:"id"`
Placement string `json:"placement"`
Label wire.Text `json:"label"`
Icon string `json:"icon,omitempty"`
Applies wire.Applies `json:"applies"`
}
ViewRow is one non-modal view (inspector/home) the explorer may offer.