@nervur-org/shell
v0.3.0
Published
The installed shell's own duties — device custody for a voice, the address book, one boot that hands back a reading engine, the deep link.
Downloads
606
Readme
@nervur-org/shell
What an installed shell owes beyond reading, as a library — nervur.com's own apps import it, and so does anyone else building an app over Nervur.
import { Custody, Shell, parsed } from '@nervur-org/shell';
const shell = await Shell({ custody: Custody() });
await shell.learn(address, { url: 'https://ground.example' });
const heard = await shell.ask(address, '{ livery }');Custody()— the voice rests in the device's IndexedDB as non-extractable CryptoKeys: it signs forever, and no page, backup or export ever reads the private bytes.Ephemeral()is the guest's custody — held for the visit, gone with it. Anything implementingopen/hold/dropserves; a native keychain adapter enters the same way.Shell({ custody })— one boot: opens (or mints and holds) the voice, wakes the address book and the kept world from custody, and hands back the engine —askthrough the observing store, thecoalescer, thedresser, the rawwire,learn/forgetfor the book, andlive.shell.live.start(onMoved?)— the live wire: long-polls each known ground's/syncwith the addresses this shell has painted, signed by the custody voice, and on a hint re-asks exactly the painted questions — the store settles, subscribers repaint,onMoved(address)fires. The hint is advice to ask again, never truth: every byte still arrives as a signed answer.stop()ends the loop.parsed(link)—nervur://dial/<address>?at=<url>&face=<livery-being>or nothing; a corrupt address is refused whole.
The shell is platform-free: Capacitor, Tauri and the plain browser all boot
it the same way, and platform events (deep links, push) are glued to
learn/ask by the app that owns them.
The reading engine
What Shell hands back is built from a few pieces, each usable on its own.
Livery resolution (Dresser) — the rule applied before any pixel wears a
color: own → authorized → default, the attestation checked first.
- Ask the ground's livery being: is this subject your resident, who dresses you, what do you wear. An unattested subject gets the default — livery applies only on the livery being's own signed word.
- A delegation (
dressedBy) is followed one hop: the authorized dresser's own livery is taken, and its own delegations are not followed — a chain answers the default. - No livery anywhere — silence, malformed documents, an undressed ground —
lands on
DEFAULT_LIVERY, never on an error.
The default livery names the design packages' own shipped theme
({ "theme": "default" }). A livery document carries names and slot values;
it never retypes the design system's own token values — the one token truth
stays design/ui/theme/tokens.css, and a theme name resolves there.
The wire (Wire) — a nervur client over HTTP, one per voice, every
answer verified from the address alone. Every answer lands on one of four
outcomes: answered (the data, signature verified) · unanswered (a
transport failure or a missing endpoint) · verify-failed (bytes arrived
but the signature or succession chain broke) · transport-failed (the
network itself threw). raise.mjs is the dev harness that births real
grounds for the wire tests and the workbench — never a shell's way to a
ground.
The store (Store, observed) — two axes, proof the line between them.
Trust: answered (the data, with my ask, my time kept beside it —
freshness is only ever mine to measure) · pending (exits by the shell's
own timer into unanswered, a fact about my patience, never the world —
and truth arriving late still lands) · transport-failed (the wire's
reach says which). The key carries the asking voice + the door + the exact
question, so two of the reader's voices never collide on one node. Lifetime:
in-memory today — visit-bound by nature — with dump/load as the
persistence seam; the view subscribes per landing.
The coalescer (Coalescer) — one ask per being per screen: each slot's
selection rides under a slot-indexed alias with its variables renamed into
the slot's namespace, so two slots on one door with different variables
coexist in one document; a document nearing the measure chunks. Failure is
per source — a silent being takes only its own slots down, an absent field
degrades its slot alone, and a refused slot (a mutation, a non-parse) never
sinks its neighbors. Queries only: coalescing is for reading, never for acts.
The workbench (node open-packages/shell/workbench.mjs, port 4600) and
the proof walk (node open-packages/shell/showcase.mjs, port 4700) —
real daemons raised, liveries created, dressed and delegated by signed
envelopes, read back as a stranger in a real browser tab.
