Moss · guides

How to do things with Moss.

Each guide is a file you can run, set in the theatre’s world. What it prints is what the installed package printed.

First steps.

The smallest app: a charter, who wears which role, and three screens, booted on a Postgres that lives in the process. The file prints the application Moss serves Linus, and somebody who is not signed in.

Install it
pnpm add @niscorp/moss @niscorp/charter @niscorp/nova @niscorp/prism @niscorp/strata @niscorp/vex zod @electric-sql/pglite
Run it
npx tsx first.mts
Write first.mts
import { PGlite } from '@electric-sql/pglite';
import { createServer, defineApp } from '@niscorp/moss';
import { createPglitePool } from '@niscorp/vex/pglite';

const app = defineApp({
  charter: {
    public: ['programme'],
    member: ['programme', 'me.*'],
    usher: ['programme', 'door.*'],
  },
  assignments: { grace: ['member'], linus: ['member', 'usher'] },
  actions: {
    programme: { id: 'programme', layout: { component: 'Text', props: { value: 'The Tempest, 2026-10-23' } } },
    'me.pass': { id: 'me.pass', layout: { component: 'Text', props: { value: 'Your season pass' } } },
    'door.list': { id: 'door.list', layout: { component: 'Text', props: { value: 'Who is coming tonight' } } },
  },
});

const db = new PGlite();
const server = await createServer(app, { db, pool: createPglitePool(db), session: 'dev-open' });

console.log(await server.catalogOf('linus'));
console.log(await server.catalogOf(null));
server.close();
It prints
{ ids: [ 'door.list', 'me.pass', 'programme' ], hash: '98205f02' }
{ ids: [ 'programme' ], hash: 'aa850b95' }

That is what GET /catalog answers each of them. session: 'dev-open' trusts any well-formed token, which suits a file like this one and never a deployment. To serve the same app, hand it to serve from @niscorp/moss/node, with a Postgres and a way to verify a session.

Then, one at a time.

Each is a file of its own and adds one thing. Read them in order, or go to the one you need.