Everything you build in Mooflair lives in a workspace: flows, campaigns, script nodes and every dataset (accounts, AI keys, tables, variables). People join a workspace as members, each with a role. The address of every app page carries the workspace: /app/<workspace>/studio, /app/<workspace>/campaign/….
How it works
- When you sign up, Mooflair makes your personal workspace (
<Name>'s workspace) and makes you its owner. /appopens the workspace you used last (else the first you belong to).- The switcher at the top of the sidebar shows the workspace you're in and your role. From it you switch workspaces, create a workspace, open Workspace settings, and (administrators) go to Admin.
Roles
| Role | What it can do |
|---|---|
| Owner | Everything, including transferring and deleting the workspace. One per workspace |
| Admin | Everything except billing changes, transferring and deleting the workspace (API keys and webhooks included) |
| Member | Sees and runs the workspace's work; creates flows, campaigns and datasets; edits and deletes only the campaigns and datasets they created. In the Studio, their role on each flow decides (Sharing a flow) |
| Guest | Only flows shared with them, with the role they were given (Sharing a flow) |
These rules live in one permission table (src/lib/workspace/permission): role × resource × action → all (everything in the workspace) or own (only what that person created). Every server action asks workspaceAccess with the permission it needs, so the same rule applies to pages, actions and the engine token. One flow in the Studio goes by its sharing instead (flowAccess); API keys keep this table.
Suspended workspaces and view-only members
- An administrator can suspend a workspace (with a reason). Everyone keeps read access; changes and runs are refused and the reason is shown.
- A member can be view-only (frozen) when a workspace has more members than seats. They can read but not change anything.
Settings (/app/<workspace>/setting)
General
- Name and address (the slug in the URL). Changing the address moves the page with it.
- Sharing: allow guests (people outside the workspace invited to one flow), allow links (anyone with the link who signs in).
- Transfer ownership (owner): to one of the workspace's admins. You become an admin.
- Delete workspace (owner): cancels its plan at the payment provider now (no more charges, the rest of the period isn't refunded; the dialog says so), stops its runs and browsers on the engine and deletes every flow, campaign, dataset, member and invite. Invoices stay. You type the name to confirm.
- Leave workspace (everyone but the owner).
Members
- Invite people by email with a role (Admin, Member, Guest). You get an invite link to send: it works once and expires in 7 days (only its hash is stored). Emails are sent automatically once notifications are set up.
- Change a member's role, remove a member, revoke a pending invite.
API keys and Webhooks
Owner and admins, Pro and up: keys for the REST API (role, expiry, shown once) and URLs that hear workspace events (signed, retried, with a delivery log). See .
- Seats: admins, members and the owner take a seat, and so do invites still waiting; guests are free. With every seat taken, inviting says so with Add a seat (the owner: Settings › Billing opens Change plan with one more seat) or Ask the owner (anyone else: the owner gets an email and a bell).
Weekly report (Settings › General, on by default, Team and up): every Monday from 08:00 UTC the owner gets last week's real runs by status, the busiest flows, and this month's use.
Log streaming (Business): an outgoing webhook with the event run.log gets every log line and status change of every run, in batches every 2 seconds, signed like the other webhooks (X-Mooflair-Signature).
Invites (/invite/<token>)
| You are | The page shows |
|---|---|
| Signed out | Who invited you to which workspace, with Sign in to accept and Create an account (the invited email filled in; both come back here; signing up with that email skips the code) |
| Signed in with the invited email | Accept (you land in the workspace) or Decline |
| Signed in with another email | That the invite is for another address, with Switch account and Ask {inviter} to invite {your email} (they get an email and a bell) |
| Already a member | Takes you straight in |
| Expired | That it ran out, with Ask {inviter} for a new invite (signed in; else Sign in to ask) and a way to your workspace. Expired invites are kept 30 days for this |
| Revoked or used | That it isn't valid, with a way to your own workspace |
Asks are sent once a day per invite (template workspace.request).
Trash
Deleting a flow, campaign, account, AI key, credential, variable or table moves it to the Trash (sidebar › Trash) for 30 days. It's hidden from every list, picker and run; a deleted campaign is turned off and its browsers stop, a deleted flow's campaigns turn off. Restore puts it back (a campaign comes back off; refused if something now has the same name), Delete forever removes it now with what belongs to it (a flow's versions, tests and agent chats; a table's rows). After 30 days the trash service deletes it for good. Deleting a workspace removes its Trash too.
How it works: a Mongoose plugin (src/lib/database/plugin/trash) adds deletedAt / deletedBy and leaves trashed documents out of every find, count, update and aggregate unless the filter names deletedAt, so the rest of the code needs no change. Admin deletes stay permanent.
Audit log
Settings › Audit log (owner and admins; Team and Business) lists who did what: invites, role changes, removals, leaving, workspace settings and transfer, billing changes and cancels, publishing, datasets created or changed, and everything moved to, restored from or deleted in the Trash. Each entry has the time, person, action, target, the change and the IP. Filter by action, Export CSV (the last 10,000). Kept a year. On other plans the tab explains it and links to pricing.
Security
- Require two-factor sign-in (Settings › General, Business): everyone must have two-factor sign-in on to open the workspace; anyone without it is sent to Account › Password to turn it on. Turning it on needs it on for yourself first.
Engine
A workspace has no engine of its own. Each flow and campaign is locked to one (engineId on it), and a workspace may use several at once. Accounts are plain data: their sign-ins live in the browsers of the campaigns using them. There is no engine picker in the header: everything goes to the engine of the thing it belongs to.
| Thing | Locked engine holds | Runs there |
|---|---|---|
| Flow | its worker (library/flow/<id>), its test browsers nobody else uses, its own runs and logs | Studio tests, Run, webhooks, schedules, MCP, chat, forms, evaluations, the AI agent's dry runs |
| Campaign | its worker (library/campaign/<id>), its browsers nobody else uses (library/browser/<id>), its runs and logs | every run of the campaign (scheduler, Run now, Test, row actions) |
| Account | nothing on the engine (its sign-ins live in browsers) | — |
Called flows run inside the caller's run, on the caller's engine.
Placement
A flow or campaign gets its engine when it's created: engineId is required, and the engine plugin (src/lib/database/plugin/engine, on both schemas) places it while validating. Every way things are made (create, duplicate, templates, imports, packages, the AI agent) goes through it, with one rule:
- The engine dedicated to the workspace, if an admin reserved one.
- Else among the shared engines its plan may use that are up and not in maintenance: the one the workspace already uses most, then those in its region, then the least loaded (things locked per browser of capacity).
When no engine can take it, creating it fails with "No engine can take it right now".
When engines don't match
A campaign or flow can only pick browsers on its own engine; moving it to another engine gives it new browsers there (sign in once).
Switching engine
Flow settings (Studio › Settings › Engine) and the campaign's Options tab have an Engine picker (owners and admins: engine.manage). Switching asks first: "Your data is on engine A. Moving deletes it there: run history, logs and signed-in browsers." (its run files and recordings go with them). Nothing is copied (engineSwitch in src/controllers/engine/switch):
engineSwitchAtis set: its runs are refused meanwhile ("It's moving to another engine").- A campaign stops (its browsers close).
engineIdpoints at the new engine. Auditengine.switch.- The
engine/cleanjob deletes its runs, logs, browsers and folder on the old engine (DELETE /app/<kind>/<id>?run=1): at once, or once it answers when it's down (retries with a growing wait).
Platform admins can empty an engine: Admin › Engine › Move everything off moves its campaigns, then its flows, each placed again by the plugin ($locals.exceptId leaves this engine out), the same way (nothing copied). What no engine of its workspace can take stays, and is listed.
Accounts sign in again on the new engine. An engine that is down or in maintenance can't be picked.
When an engine is down
No failover. When the monitor marks an engine down, every workspace with something on it gets a notification ("The engine A is down"), and another when it's back. Meanwhile:
- Every /app page shows A is down above the content (an engine on another runtime refuses the run itself, with its reason).
- Run, Test run and Open browser (Studio), Run now and Test (campaign) are disabled; starting a run anyway is refused with "The engine A is down".
- The scheduler skips its campaigns (no restart is counted) and picks them up when it's back.
- Resume links and forms answer
503("try again later"). - Lists don't ask it: the Runs page and Insights show the other engines' runs and name the engine that is down or didn't answer.
- Files it holds show On engine A, which is down in Dataset › Files (no download); the Drive node reading one fails with "…is on another engine that isn't answering right now".
Region (Enterprise)
Settings › General › Region lists the regions of the engines the workspace may use (shown with the engine.region feature and at least one region set on an engine). It's a preference for placement: new flows, campaigns and accounts get an engine there. What is already locked stays (switch it in its settings).
Across engines
- One token for every engine: mooflair signs the page's workspace token with its own key (
secret.engine.key, ES256); each engine checks it with mooflair's public key, set in itsMOOFLAIR_KEY(from mooflair'slibrary/configuration.json,secret.engine.key.public). See . - Lists: the Runs page and Insights (the engines are read on each visit, so a first placement shows at once), Studio's Runs window, the weekly report, run history pruning and
GET /api/v1/runask every engine the workspace uses and merge (newest first). Pages that belong to one thing (a campaign, a flow's live run, a browser) ask only its engine. - Plan and workspace limits: browsers at once (the plan's) and the workspace's RAM and CPU limit (set by an admin) are one lease in mooflair: each engine reserves room right before it starts a container, in one atomic check across every engine, and releases it when it stops (). Steps and browser hours were already summed in mooflair.
- Files: the Drive keeps records in mooflair and bytes on the engines (next-drive 8,
MAIN/NODE); an engine reading a file another one holds fetches a copy, so every engine must reach the others'ENGINE_PUBLIC_URL.
Notifications
Workspace events (a flow's Notify node, a campaign that was cut off or resumed, scheduler messages) go to the bell of the workspace's owner and admins, plus whoever created the thing they're about.
Admin
Administrators manage every workspace in .
FAQ
Do I need to create a workspace?
No. You get a personal workspace when you sign up. Create more from the switcher when you want a separate place with its own members.
Can a member see my datasets?
Members see and can use the workspace's datasets in their flows; they can only edit or delete the ones they created. Owners and admins manage all of them.
What happens to my stars?
Stars are per person: starring a flow doesn't change it for anyone else.
What does deleting a workspace remove?
Everything in it: flows and their versions, tests and agent chats, campaigns and their browsers, datasets and table rows, members and invites. Past runs stay on the engine until their retention ends.