Wayseer.dev

Wayseer Desktop · modules

A module reads one source and tells the app what is there.

This is the overview: what a module is, the contract it keeps, how it runs as a program of its own, how it is signed, and what its manifest promises. The full guide, with the code, follows once the SDK is published. Wayseer Desktop itself is pre-release.

What a module is

Four things, and nothing else.

Wayseer Desktop knows only entities (a host, a service, a database, a queue), edges between them (runs on, depends on, talks to), series (one metric of one entity over time) and events (something that happened to an entity). A module reads a source and turns it into those four things. It owns what it sends: the app links entities from different modules that are the same thing, and draws the rest.

An entity has a kind. The core kinds, such as host, service, container, database and queue, are drawn with their own glyphs, so a module uses one when it fits. Otherwise it defines kinds in a namespace of its own, such as acme/rack; the namespace comes from the developer's certificate, so no module can make entities that look like another's.

A module is written in Go against the SDK. It imports one package, and its tests run a conformance suite that checks the contract below against fixtures, never a live source.

The contract

Four calls, a snapshot, then deltas.

The app makes one module per entry in the owner's config and runs it until the app stops. Everything else is optional.

  1. Info

    The module's kind, a version and a one-line description.

  2. Configure

    Runs once. Decodes the entry's options, which are strict (an unknown option is an error that names its line), sets defaults, reads any secret. No network or disk I/O that can hang: an error here stops the app at start-up with the line to fix.

  3. Run

    Runs until cancelled. The first thing it sends is a snapshot, everything it owns; after that, deltas, only what changed, on a steady beat, empty when nothing changed, so a missed send shows as stale. It returns within a second of cancellation.

  4. Health

    Called at any time from another goroutine, so it is cheap: the last error, whether the source is unreachable, or a note worth showing.

Optional interfaces add what a module can answer on demand: Discover returns the whole state now; series queries answer for a metric catalogue over a time window; event queries answer about past events; search finds entities by text; subscriptions stream series as they change; and a module that can rank, as Prometheus can, says so and answers only for the top entities.

A module may offer actions on its entities, such as restarting one. Parameters are typed and bounded, there is no free text, and nothing runs until the owner has allowed the action in config and confirmed it on the day. The action must also be declared in the module's manifest.

Secrets never come from the options themselves: the owner names a file, an environment variable or a keyring entry, and the SDK hands the module a value that prints as redacted everywhere. A module's errors reach the screen and the log, so they say what failed and where, and never a secret.

External modules

The same module, as a program of its own.

Wayseer's own modules are built into the app. A module you write runs as a separate program, with a one-line main that serves it. The app starts the program, talks to it over a private local connection with the same calls as above, and starts it again, with back-off, if it fails. The program sees only the environment the owner's config lists.

Two rules hold for every external module. It needs a licence key that unlocks external modules; without one the program never starts, and the app shows the instance as locked and says why. And only a signed, installed package runs: a program named directly in config never does, not even in development. Developers sign their own modules under a developer certificate.

A module in another language can serve the same contract, but the SDK, the template and the conformance suite are Go, and so is the marketplace for now.

Certificates and signing

Your key, certified by Wayseer, bound to your licence.

The private key is made on your machine and never leaves it. Wayseer certifies the public half.

A developer certificate lets you run modules you wrote and signed yourself, under your own licence. The app's dev keygen command writes a private key to a file only you can read and prints the public key. Wayseer issues a certificate for that public key: it holds your licence ID and your namespace, it is not secret, and it does not expire. A renewed licence keeps its ID, so the certificate keeps working.

A module signed with your key runs only where your licence key is active, and under no other licence. There are no organisation-wide certificates; one licence may hold several. A lost key cannot be recovered, only replaced by a new key and a new certificate.

dev sign takes the built program and its manifest, fills in the program's hash and platform, checks the manifest and that the key is your certificate's, and writes one package file per operating system and architecture. At every start the app checks the licence, the signature chain, the certificate's licence ID, the program's hash, the revocation list and the contract version, in that order, and refuses with one plain reason if any fails.

Asking for a certificate. How a licensed developer sends Wayseer their public key, and gets the certificate back, is not settled yet. It will be published here. Until then there is no address or form to use, and nothing on this page should be taken as one.

what signing producesone package per platform
acme-widget-0.3.0-linux-amd64.wsmod.tar.gz
├─ module          the program
├─ manifest.yaml   what it is and may do
└─ signature       over the manifest, by your key

The manifest

What the module is, and what it may do, in writing.

The app enforces it. The owner reads it, in the sources panel, before the module ever runs.

manifest.yamlstrict: only these fields
id: acme/widget           # publisher/name
name: Widget
description: Racks and their power use.
homepage: https://example.com/widget
version: 0.3.0
contract: 1               # the contract's major version
namespace: acme           # your certificate's
kinds:
  - {kind: acme/rack, label: Rack, plural: Racks, like: host}
actions:
  - {id: recheck, title: Recheck, changes: Reads the inventory again now, kinds: [acme/rack]}
network: [configured by user]
secrets: true

The manifest is strict YAML with a fixed set of fields. The signing step fills in the program's hash and platform; a marketplace package also carries its source commit and its publisher's certificate.

What the app enforces: every kind the module sends is a core kind or in its namespace, and a change set with any other kind is refused whole; only the actions listed here, on the kinds named here, are ever offered, and the owner still confirms each; the contract version must match the app's major version. The network endpoints are disclosure, shown to the owner but not enforced, and so is whether the module reads the keyring.

Namespaces are allocated by Wayseer with the certificate, never chosen, and have one owner in an install. The first-party names are reserved.

A newer version can't widen what a module does unseen: before the owner installs it, the app lists what its manifest adds and drops, such as new actions, kinds or endpoints.

The marketplace

Public source, built and signed by Wayseer.

A module signed under a developer certificate runs only under that developer's licence. To run on any licensed install, a module goes through the marketplace: the publisher, who holds a licence and a certificate, submits a public source tree at a tag; Wayseer reads the change since the last approved version, builds it reproducibly, runs the conformance suite against the result, checks the manifest against what the module reports, signs it with the marketplace key, and publishes the binary with its manifest, source link, commit and build recipe. Because the build is reproducible, anyone can rebuild from source and compare.

Publishers cannot charge for a marketplace module, and there is no revenue share. The source must stay available for every published version. Marketplace modules are always external programs, never built into the app, and are Go only; builds are for Linux first.

Any module can be revoked: one version, a range, everything under a certificate, or everything signed by one key, for security, for being unmaintained or unstable, or at the publisher's request. The app holds a signed revocation list, fetches a newer one in the background without sending any identifier, and deactivates a revoked module at once, telling the owner why in plain words. The marketplace does not exist yet; this is the design the app is being built to.

What is coming

What this section will hold, when it can.

In the order the work is planned. No dates; the app is pre-release and the work is sequenced behind it.

  • The SDK, public done

    The SDK and the module contract as their own MIT-licensed Go module at wayseer.dev/sdk, with a compatibility promise once the platform ships. Public now, at v0.

  • A template done

    A public starting point for an external module: the module, a one-line program that serves it, a starter manifest, the conformance test, and the signing step. Start from the template; it is MIT-0, so a copy owes nothing.

  • The guide

    The app's own guide to writing a module, with the code: the lifecycle, entities and kinds, events, series, health and back-off, secrets, actions, testing, signing. This page is its overview.

  • Reference modules done

    The free first-party modules, each public under wayseer.dev/modules/… (self, localhost, file, prometheus), as working examples of the contract.

  • Certificates

    The way to ask for a developer certificate with your licence and public key, and to get it back.

  • The marketplace

    Submitting, review, the publisher agreement, and the published list of modules with their manifests.