Four roles, one definition
Owner, admin, member, viewer, defined once in a shared package so the API and the dashboard cannot disagree about what a member may do. Project settings, goals, alerts, channels, sharing, keys and team membership all check it independently on the server.


Invitations that a forwarded link cannot exploit
Tokens are stored hashed, expire after seven days, and acceptance is bound to the address the invitation was sent to, so forwarding the email grants nothing. Membership and consumption of the invite happen in one transaction, so a half-accepted invitation is not a state that exists.


The last owner cannot lock everybody out
The only owner of a workspace cannot be demoted or removed. It is a small guard that prevents the specific irreversible accident of an organisation with data and nobody able to administer it.


A read-only link for people who should not have an account
A property summary can be published at an unguessable token, for a client, an investor or a public transparency page. It is scoped to that property and read-only, and it can be revoked from settings.


How it works, precisely
- Roles
- owner > admin > member > viewer, defined in @falorb/db/roles
- Enforcement
- Re-derived server-side on every mutation
- Invitations
- Hashed token, 7-day expiry, bound to the invited address
- Public link
- Per-property, read-only, revocable
Next, most people read
Run it yourself this afternoon
Clone the repository, bring up the stack with Docker Compose, paste one script tag. The data never leaves your machines.

