Making, packing and installing plugins
notesy plugin does everything around a plugin from a terminal, without
opening a window: starting one, checking it, working on it with a running
notesy, packing it into one file, and installing that file.
notesy plugin new you.word-goal a new plugin folder
notesy plugin check everything wrong with it, with lines
notesy plugin dev link it into notesy; it reloads as you save
notesy plugin keygen your key, to sign your packages with (once)
notesy plugin pack you.word-goal-0.1.0.notesy-plugin, signed
notesy plugin signers add KEY let a teammate's key (or your new one) sign its updates
notesy plugin install FILE install a package
notesy plugin uninstall you.word-goal remove one (--keep-data keeps its data)
#Starting one
notesy plugin new you.word-goal makes a folder you.word-goal with a
plugin.toml (plugin.toml), a main.rn that says hello in
its log when it's ready and each time a note is saved, and a readme.
notesy plugin new you.word-goal plugins/goal puts it elsewhere.
#Checking it
notesy plugin check (in the plugin's folder, or check FOLDER) reads
the manifest, checks every file it names is there and that the folder can
be packed, and compiles the script against exactly the modules its
permissions allow. Every problem is listed, with the file and line for
the script's. A script that uses a module it didn't ask for doesn't
compile:
error: Missing item `notesy::status`
┌─ main.rn:1:5
│
1 │ use notesy::status;
│ ^^^^^^^^^^^^^^
(status takes the ui permission: add it to permissions.)
#Working on it
notesy plugin dev links the folder into notesy's plugins folder (a
symbolic link, so you keep working where it is); Settings, Plugins, "Load
a plugin folder" does the same from the window. Nothing is packed or
signed. Its row on the Plugins page says where it's linked from and has a
Reload button, and notesy looks at its files once a second: save any of
them (a script, plugin.toml, a theme, an icon) and it's read again and
starts afresh, saying so in its log, with every error and its line.
Removing it only unlinks it: the folder stays where it is.
#Previewing it
notesy preview shows one screen of notesy for a look, and --plugin
loads yours into it, straight from its folder:
notesy preview --plugin . plugin-page its page in Settings
notesy preview --plugin . plugin-review the prompt asking to let it run
notesy preview --plugin . view board a kind of tab its script adds
notesy preview --plugin . panel tasks a panel its script adds
notesy preview --plugin . palette its commands, among notesy's
view and panel open once its script has added them. In a preview it's
allowed what it asks for, for the look only: it runs over a few sample
notes (never yours), keeps its data in a temporary folder and its secrets
nowhere, and nothing it's allowed is saved. What reaches beyond the
preview is held back: the network (and so signing in), reading the
clipboard and opening links. Its script still compiles, each call is an
error saying why, and its log says what's held back; --trust lets it
have them. Your installed plugins don't run in a preview, and a preview
installs, links and removes nothing.
--plugin also takes an installed plugin's id, and can be given more than
once. --record DIR saves the screen frame by frame, and notesy preview --list lists every screen.
A plugin can also name screens of its own in [[previews]], set up by its
script (notesy::preview): notesy preview ana.tasks/board --plugin . shows one, and notesy preview --list lists them with notesy's.
#Signing and packing
notesy plugin keygen makes your key, once: an ed25519 key kept in
notesy's config folder as signing.key, readable only by you. It prints
the public half (what you give a teammate) and a short fingerprint for
telling keys apart. Back it up. On a build machine, give the key in
NOTESY_SIGNING_KEY (its 64 hex digits) instead.
notesy plugin pack checks the folder, then writes
<id>-<version>.notesy-plugin in the folder you're in (--out DIR puts it
elsewhere), signed with your key. --unsigned leaves the signature out;
notesy still installs those, and says they're unsigned.
A signature says a package is exactly what its signer packed, and it's what keeps updates honest: once someone has your plugin, notesy takes an update of it only when a key it trusts for that plugin signed it. It doesn't say who you are; a key isn't a person, so notesy never shows one as a name. A registry will (plan.md).
#Who may sign its updates
A plugin starts with one signer: the key its first package was signed
with. signers.toml, in its folder and packed with it, changes that. It's
a log of keys added and removed, each change signed by a key that could
sign the plugin's packages when it was made and linked to the one before
it by its hash. notesy walks it from the keys it trusts, so a key comes
in only through one already trusted, and a removed key can't sign again.
notesy also remembers how far into the log it has read, and an update's
signers.toml has to carry on from there: no change it took can be cut
from the end or written again, so a key can't come back by leaving its
removal out. Only ever add to the log; never start it over.
notesy plugin signers list who may sign its updates now
notesy plugin signers add KEY let another key sign them too
notesy plugin signers remove KEY stop one (its key, or its fingerprint)
For a team: a teammate runs notesy plugin keygen and gives you the
public key it prints; you add it and commit signers.toml, and from the
next version either of you can pack and publish. A plugin keeps at least
one signer.
For a new key of your own, while you still have the old one (it may have
leaked, or it's simply time): notesy plugin keygen --rotate makes a new
key and keeps the old one as signing.key.old; notesy plugin signers rotate in each of your plugins' folders adds the new key (signed by the
old) and removes the old (signed by the new). Publish a version of each
with its signers.toml, then delete signing.key.old: anyone with a
version after the handover refuses updates signed with the old key. A key
that's lost for good can't hand over; a teammate's can still remove it and
add your new one, which is a reason to have two.
#What's in a package
A package is a zip:
| Entry | What it is |
|---|---|
PACKAGE |
notesy-package 1, the plugin's id and version, then a line per file: its SHA-256, its size and its path |
SIGNATURE |
format = "notesy-signature 2", then a [[signature]] for each: its role (author; a registry's will be registry), its public key and its sig, in hex, over notesy package 2, the role, and PACKAGE's SHA-256. (The first packages' ed25519 <key> and a signature over PACKAGE still read.) |
| everything else | the plugin's folder, as it is |
A signers.toml goes in like any file, so PACKAGE covers it, and it's
checked whole when the package is read: it has to name the key the
package is signed with. Files are sorted and stamped with a fixed time, so
the same folder always packs to the same bytes. Hidden files and folders (.git) and packages in
the folder stay out.
A package (and a folder being packed) can't have:
- paths out of the plugin's folder (
.., absolute paths); - links;
- native code or things that run (
.so,.dll,.dylib,.exe,.wasm,.sh,.bat,.ps1…): plugins are Rune scripts and data; - more than 2,000 files, or more than 20 MiB unpacked.
#Installing
Drop a .notesy-plugin on notesy's window, pick one with Settings,
Plugins, "Install a plugin", or run notesy plugin install FILE.
notesy reads the whole package first: every file has to be in PACKAGE
with its size and hash, nothing else can be in it, the signature has to
match, and its plugin.toml has to be the one PACKAGE names. Anything
wrong and it isn't installed, and notesy says what.
Then it says whether it's signed, and by what: the same key as the
version you have, a key that version handed over to, or a key new to
notesy (which nothing vouches for yet); and whether it runs anything, to
confirm. The key's fingerprint is under that, for comparing. It's unpacked into a folder beside
the plugins and moved into place in one step, so a failed install leaves
nothing behind. Its PACKAGE and SIGNATURE stay beside its files, and
the Plugins page shows it as signed only while its signature holds and
every file is still the one signed. A plugin that runs a script then asks
for what its manifest declares, as any plugin does, before it runs.
#Updating
Installing a newer version of an installed plugin replaces it and keeps its
data. The first time a plugin is installed from a signed package, notesy
remembers the keys that may sign it (the one it was signed with, or the
ones its signers.toml names) and how far into that log it read, in
plugins/grants.toml. An update has to be signed by one of them, as its
own signers.toml leaves them (carrying on from where notesy read to), or
it's refused, saying why; an unsigned update of a signed plugin is refused
too. An update that asks for more than before asks again ("Review update").
An older version isn't installed over a newer one: it could bring back
what the newer one fixed. Remove the plugin first to go back, or run
notesy plugin install --downgrade FILE.
#Removing
Settings, Plugins, a plugin's "Remove" (or notesy plugin uninstall ID)
stops it, takes away everything it added, and removes its folder. It
asks whether to keep its data (its store, and its secrets in the system
keychain) for next time, or remove that too; notesy plugin uninstall
removes both unless --keep-data.
Every page
- Overview
- plugin.toml: Every field of plugin.toml
- Permissions: What a plugin can ask for, when notesy asks, and what changes it
- Scripts: How a script runs: its lifecycle, events, limits and errors
- Packing and installing: Making, checking, signing, packing and installing
- notesy::log: Lines for its log on the Plugins page
- notesy::events: Hearing what happens, and every event
- notesy::commands: Adding commands, and running notesy's
- notesy::store: Keeping its own data
- notesy::settings: Reading its settings
- notesy::secrets: Keys and tokens, in the system keychain
- notesy::notes: Reading and changing the vault's notes
- notesy::editor: The note in front
- notesy::files: A folder of its own
- notesy::links: Opening pages in the browser
- notesy::clipboard: Copying and pasting
- notesy::view: What panels, tabs and sections show
- notesy::panels and notesy::views: Adding side panels and tabs
- notesy::sections: Sidebar sections
- notesy::menus: Items in notesy's right-click menus
- notesy::status: Status bar items
- notesy::notices: Notices
- notesy::dialogs: Asking in a dialog, notesy's kinds or its own
- notesy::cards: Its part of the tree's hover cards
- notesy::boards: Boards' cards and arrows, and kinds of card of its own
- notesy::blocks: Drawing its fenced blocks
- notesy::icons: Drawing its own icons
- notesy::net: Requests to the sites it names
- notesy::accounts: Signing in to a service
- notesy::json: Reading and writing JSON
- notesy::toml: Reading and writing TOML
- notesy::yaml: Reading and writing YAML, and a note's front matter
- notesy::time: Now, written in the user's time zone, dates read, how long ago
- notesy::math: Trigonometry and the like, for drawing
- Names: Icon, Color, Tone, Side, Method, Command, Menu, Sidebar, Event: Notesy's names as enums
- notesy::preview: Its own screens for notesy preview
- notesy::perf: Timing its own work
- notesy::tex: Math macros for every note's formulas