Skip to content

An organization, its applications and one login

An organization is one tenant: one SQLite file, one image directory, one address, one set of logins. An organization can hold more than one application, and each application answers under its own first path segment.

https://sampletown.matterdata.example/club/members/17
└─┬┘
the application's mount

Read App mount for the declaration and its refusals.

A login belongs to the organization. A role belongs to one application of it, so one person can be the treasurer in the club application and a reader in the calendar.

This decides what the reserved role authenticated means:

That reading holds whether the organization has one application or five. A rule whose meaning changed when somebody installed a second application would be a rule you cannot reason about.

A refusal is still attributed. Somebody denied a page of an application they hold no role in is anonymous for the access decision. The audit event still names them. Those are two different questions, and answering them with one value would give either a leak or an unattributable refusal.

What the applications of one organization share

Section titled “What the applications of one organization share”

Everything in the list below is the organization’s, not an application’s.

Shared Consequence
the SQLite file and the image directory one backup covers every application
the logins and their sessions signing in once reaches every application
the CSRF token, the color scheme and the reader’s time zone one browser cookie jar
the 512 MiB image quota one application’s uploads use up another’s room
the audit log one trail. An event of an application names it; a sign-in names none
the administration page at /matterdata one door to the organization’s logins

Each application keeps its own tables, roles, menu and hook modules. It also keeps its own declared time zone, and its own value for an application-wide scope.

Table names do not collide. Two applications can each declare events, because the engine stores a table under <application name>__<table name>. Your .mtd files, a ref(...), a hook’s ctx.db.query(...) and every error message keep the short name.

The trust boundary is the organization, not the application

Section titled “The trust boundary is the organization, not the application”

Two applications of one organization are one origin, and the session cookie carries Path=/. A browser cannot keep them apart.

That sharing is the feature. It is also the boundary, and it decides one design question:

  • Applications used by the same people → one organization. A club’s member register and its calendar. Everybody signs in once, and each application grants its own roles.
  • A public site and an internal one → two organizations. Two tenants, two files, two addresses, two sessions. Nothing a mistake in one can reach in the other.
  • Applications that share data → one application. A mount is an address, not a join. An application cannot read another application’s tables, and no hook can either.

The address / of the organization lists the applications this person can open. Each entry links to the first page of that application they may open, and names the role they hold there.

Three rules decide what happens at /:

  • One application in the organization: / redirects to it. The redirect is decided before the visitor is considered, so the address does not reveal who is asking.
  • Several applications: / lists them. An application in which this person can open no page is left out, rather than shown and refused.
  • An application declares mount: /: that application answers /, and there is no list. If it declares no page at route: /, then / answers 404.

Nothing reads your data to build the list. It discloses that an application exists and what it is called, which its address already discloses to anybody who guesses it.

Every address the engine serves begins with /matterdata. Most of them stay at the organization’s root, because they belong to the organization: /matterdata/login, /matterdata/logout, /matterdata, /matterdata/appearance, /matterdata/timezone, /matterdata/static/… and /matterdata/theme.css.

Two engine addresses belong to one application and sit under its mount, as /<mount>/matterdata/…: scope, which switches an application-wide scope, and blob/…, which serves an image. Both resolve a name against one application’s model, and at the organization’s root that name would have two meanings.

The frame draws no switcher between applications, and a root-relative link written in Markdown leaves the application it was written in. Read Project status before you plan an organization around several applications.