Platform model — two products, three authorities, one daemon [Concept]
From the Meridians Wiki · Public · Maintained · joint
The control plane around a running instance. Four ideas that are easy to conflate — kept apart here. Shipped code: src/app/(gateway), src/features/gateway, src/lib/server/gateway, supabase/migrations/, electron/. Design doc: GATEWAY.md 🔒. Enforcement: PERMISSIONS.md 🔒.
Two products, one account
A director can hold License AND/OR Hosted. Products are per-subscription rows, never a column — so "both at once" is a first-class case, not an edge.
flowchart LR
D["**director**<br/>one per person<br/>· one Stripe customer"]
D -->|"0..1"| L["subscription: **license**<br/>BYOK · self-hosted"]
D -->|"0..1"| H["subscription: **hosted**<br/>VM · provider usage included"]
L -->|"hasLicense"| App(["Electron app<br/>runs on your machine"])
H -->|"hasHosted"| VM(["Fly VM<br/>always-on host + its own domain"])
D -.->|"in platform_admins?"| Admin(["platform operator"])
Three authorities — don't conflate them
"Director" names three related-but-separate things, and each answers a different question. Supabase authenticates the person; it says nothing about the other two.
flowchart LR
subgraph Platform["Platform account · Supabase"]
PR["**platform role**<br/>owner › admin › director › guest<br/>= WHAT you are on the platform"]
PA["directors + subscriptions<br/>= which PRODUCT you own"]
end
subgraph Instance["In-instance people · access-context"]
GM["instanceMembers<br/>Guest→Viewer→Contributor→Manager→Director"]
end
PA ==>|"the account owner IS<br/>the instance Director"| GM
PR -.->|"gates: the cross-Director<br/>admin plane + owner SQL"| CP(["control plane"])
PA -.->|"gates: can you run<br/>this instance type"| RT(["a running instance"])
GM -.->|"gates: what you may<br/>DO inside it"| RT
| Authority | Question it answers | Cardinality |
|---|---|---|
| Platform role | may I administer the platform? | one owner per gateway — so one across the whole product; admin is the invitable tier |
| Product entitlement | may I run this instance type? | per-subscription, both products can be live at once |
| In-instance people | what may I do inside this instance? | one Director per instance — it is one person's record |
Platform role = operator authority; account = ownership + billing; in-instance people = collaboration roles. The gateway never touches instanceMembers; the instance never touches Supabase billing.
Knowing the platform role
Because the role lives only on the gateway, an instance has to ask — and every headless caller must get the same answer. So one resolver writes the verdict through onto the daemon's copy of the Director session, and everything reads it from there.
flowchart LR
SI["Director signs in<br/>(browser · Supabase)"] -->|"session push"| DS[("directorSession.json<br/>local-only, never replicated")]
DS -->|"resolve once"| GW{{"GET /api/account/session<br/>role · admin · owner · entitled"}}
GW -->|"cached with checkedAt"| DS
DS --> MCP(["MCP scope gate<br/>director / admin / owner"])
DS --> CR(["control-plane reads<br/>owner/admin only"])
DS --> Diag(["session diagnostics<br/>never a token"])
The cache is advisory and fresh-only (5-minute TTL): a role that outlived a revocation is never re-readable, it survives token rotation (rotating a token is not a role change), and it never outlives the session it belongs to. It buys latency and good error messages — never authorization. The remote route re-checks every call.
One daemon, two hosts
The server-of-record (E1) is one host-agnostic Node daemon — record + engine + scheduler + loops. License forks it locally; Hosted boots the same code on a VM. Only where it runs and how the browser reaches it differ.
flowchart LR
E1["**E1** server-of-record<br/>host-agnostic daemon"]
E1 -->|"Electron forks it<br/>(License)"| Local["localhost:41999<br/>record → userData"]
E1 -->|"a Fly VM boots it<br/>(Hosted)"| Fly["<slug>.meridians.global<br/>record → /data volume"]
Local --> LC(["local renderer<br/>always-connected"])
Fly --> RC(["remote browser<br/>reconnect + sync"])
Shipped: the managed Fly VM runtime, entitlement model, gateway app, control-plane schema, and Electron host. The hosted runtime lives in
src/lib/server/program/hosted-runtime.ts+src/lib/server/gateway/; provisioning/fleet in the gateway console. Sign-in mechanics: flows/hosted-signin.