Charter · guide

An app built from what is granted

The screens a person resolves to, handed to a Nova shell. The shell is asked for one it was given and one it was not.

Install it
pnpm add @niscorp/charter @niscorp/nova @niscorp/prism @niscorp/strata zod
Step 1 of 4

Three screens, and a charter

Each screen is a Nova action: an id and a layout. Together they are everything the app ships.

The charter gives a member two of them.

Three screens, and a charter
import { resolvePrincipal } from '@niscorp/charter';
import { createComponentRegistry, createShell, type ActionDefinition } from '@niscorp/nova';

const shipped: Record<string, ActionDefinition> = {
  programme: { id: 'programme', layout: { component: 'Text', children: 'This week' } },
  seats: { id: 'seats', layout: { component: 'Text', children: 'Pick your seats' } },
  takings: { id: 'takings', layout: { component: 'Text', children: 'Tonight: 4,180' } },
};
const charter = { member: ['programme', 'seats'], manager: ['*'] };
Step 2 of 4

Keep what is granted

The member’s roles are resolved over the ids that exist. The app for this person is the screens in that set, and only those.

Keep what is granted
const granted = resolvePrincipal(charter, Object.keys(shipped), ['member']);
const theirs = Object.fromEntries(Object.entries(shipped).filter(([id]) => granted.has(id)));
Step 3 of 4

Build their shell

A shell is made from the screens it is handed. This is what Moss does on the server for each person who signs in, and it is where the charter is enforced.

Nova itself has no charter. A shell made in a browser from everything the page ships holds everything. The enforcement is that the server never puts the other screens in.

Build their shell
const shell = createShell({ canvases: [{ id: 'main' }], registry: createComponentRegistry(), actions: theirs });
Step 4 of 4

Ask for two screens

A screen is opened by its id. The one the member was given opens. The other is not hidden or locked: the shell has never heard of it, and says so.

Ask for two screens
for (const id of ['seats', 'takings']) {
  try {
    shell.push('main', id);
    console.log(id + ': opened');
  } catch (error) {
    console.log(id + ':', error instanceof Error ? error.message : error);
  }
}
Then

Run it.

The screen that was granted opens, and the one that was not is unknown to the shell.

Run it
npx tsx app.mts
It prints
seats: opened
takings: Unknown action: takings