Editions and surface profiles
Compare the Community, Business and Cloud editions, switch a self-hosted Nodaro to Business, and narrow its interface with a NODARO_SURFACE_PROFILE.
Nodaro has three editions — Community, Business and Cloud — built from the same code and selected with the EDITION variable. On the Business edition, a surface profile then narrows what the install shows, without a rebuild. It can hide parts of the interface, remove nodes and models, rename the product and restrict sign-in. This page covers both.
The three editions
| Community | Business | Cloud | |
|---|---|---|---|
| Self-hostable | Yes | Yes | No, managed by Nodaro |
| Admin panel | No | Yes | Yes |
| User management | No | Yes | Yes |
| Credit ledger | No | No | Yes |
| Billing | No | No | Yes |
| Credit prices set by admins | No | No | Yes |
| Surface profile | Ignored | Yes | Yes |
- Community (
EDITION=community, the default) is the free self-hosted edition. Anyone who signs up becomes a regular user. - Business (
EDITION=business) adds the admin panel and user management. It is still self-hosted and still has no billing. - Cloud (
EDITION=cloud) adds credits and billing. It powers app.nodaro.ai and is not meant for self-hosting.
The Business edition's admin panel and user management are Enterprise features. You may run them for development and testing, but using them in production requires a Nodaro Enterprise subscription. See License.
Switch a self-hosted install to Business
The API reads EDITION at startup. The editor in the browser reads its edition from VITE_EDITION when the image is built. The published image is built as the Community Edition, so a Business install is built from source.
Moving from Community to Business has no database cost: the schema is the same.
Change both values in the compose file
In docker-compose.community.yml, under the nodaro service, change the two lines that say community:
nodaro:
build:
args:
VITE_EDITION: business # was: community
environment:
EDITION: business # was: communityBoth lines are fixed in the compose file, so setting EDITION in .env has no effect.
Build and start
docker compose -f docker-compose.community.yml build --no-cache nodaro
docker compose -f docker-compose.community.yml up -dUpdate a source-built install with git pull and build. docker compose pull would download the published Community image again.
The build refuses an empty or unknown VITE_EDITION. Without that check, an unset value would build cleanly and fall back to community, and a Business image would ship without its admin panel. The compose file passes the value for you. A hand-written docker build needs --build-arg VITE_EDITION=community, business or cloud.
What is fixed at build time and what is not
Three browser settings are not fixed at build time: the API address, the browser's sign-in address and the anon key. At boot, the container writes them into /config.js from PUBLIC_URL, FRONTEND_SUPABASE_URL and SUPABASE_ANON_KEY, and the browser reads that file before the app starts. So the published image serves any port or domain after a restart. The VITE_* build arguments are only fallbacks for unset runtime values.
Other build-time browser settings:
| Build argument | What it does | Default |
|---|---|---|
VITE_STUDIO_URL | The address of the Studio app, for Open in Studio links | https://studio.nodaro.ai |
VITE_PERSON_URL | The address of the Person app, for the Open Person card on the home screen | https://person.nodaro.ai |
Surface profiles
NODARO_SURFACE_PROFILE narrows the interface of a Business or Cloud install without a rebuild. The Community Edition ignores it and always shows the full interface.
- Format. Inline JSON, or
@/path/to/profile.jsonto read a file. To use a file on the compose stack, mount it into the container and give its path inside the container. - Narrow only. A profile can hide and remove. It never enables something that the edition disables.
- Every field is optional. An empty list means "keep the default". Unset, the install shows the full interface.
- Errors. A malformed field falls back to that field's default, with a warning in the log. A profile that does not load at all stops a Business or Cloud install from booting, with
[surface-profile] FATAL … Refusing to boot a narrowing deployment mainline-open. That happens with an unreadable file, invalid JSON, or a value that fails validation as a whole. A narrowing install must never come up showing everything. - Apply. Restart the app. The profile reaches the browser through
/config.js.
Example
NODARO_SURFACE_PROFILE={"nav":{"hide":["gallery"]},"brand":{"productName":"Studio"},"outputs":{"allowPublic":false},"voice":{"allowedGenders":["male"]}}This hides the gallery, renames the product to Studio, keeps every output private and offers male voices only.
Fields
| Field | What it does |
|---|---|
nav.hide | Hides sidebar entries. Values: gallery, explore, pricing, templates, apps, community, integrations. |
dashboard.tabs | One ordered whitelist of the home screen's sections. See Home-screen sections. |
nodes.deny, models.deny | Node types and model IDs to remove everywhere: the node picker, GET /v1/nodes, GET /v1/models, the MCP tools, and at run time. A removed node fails with node_not_available. |
nodes.allow, models.allow | Whitelists. When a list is not empty, only the listed node types or model IDs are offered, and deny still subtracts from them. See Allow lists. |
auth.methods, auth.ssoLabel | The sign-in methods to offer: email, google, sso. sso is dropped unless auth.ssoLabel is set. See Sign-in methods. |
siblings.apps | Replaces the Nodaro links in the product switcher: [{ "label": "...", "url": "..." }]. |
brand.productName | Replaces the wordmark and the page title. Absent, the page title stays as shipped. |
brand.wordmark | Short text shown beside your own logo in the sidebar header, for an install that ships its own logo files. For example, product name Acme Studio with wordmark Studio. |
brand.description | Replaces the page's meta description. |
brand.platformLinks | false hides the platform's own legal, documentation, release-note and product-promotion links. Links you set in siblings.apps stay. |
locale.default | The language a new visitor starts in. |
locale.picker | false hides the language picker. The default is true. |
outputs.allowPublic | false makes every output private, whatever the user chooses. |
voice.allowedGenders | Restricts voices to any subset of male, female and neutral. Empty means every gender. See Voice genders. |
features.hide | Switches whole features off: copilot removes the Workflow Copilot, and presentation hides the canvas Present tab. |
catalogs | { "required": true, "factoryPresets": false } for installs that need reviewed picker content. See Picker catalogs. |
Logos and the favicon are not part of the profile. Replace them with a layer of static files in your Docker image.
Home-screen sections
dashboard.tabs is one ordered whitelist. The full key set is workflows, projects, apps, miniapps, templates, tutorials, statistics, gallery, studio and mcp.
workflows,projects,studio— the lists of the Continue tab's workspace filter, in the order of your list. If your list names none of the three, all three show, so the main list is never blank.apps— the Nodaro apps band on Continue.templates— the "Start from a template" row on Explore, and the Templates sidebar entry.tutorials— the Level up tutorials on Explore, and the Tutorials sidebar entry. Withouttemplatesandtutorials, the Explore tab is not shown.miniapps— the MiniApps sidebar entry.statistics— the statistics overview on the Executions page.mcp— the fourth "Jump back in" list: the workflows an MCP client created in its automaticmcpproject.
List every section you want to keep. For example, ["workflows","projects","statistics","tutorials"] keeps the workflow and project lists, the tutorials and the statistics. It drops the Studio list, the apps band, the template row and the Templates and MiniApps sidebar entries. nav.hide still applies on top.
Allow lists
An allow list is the safer shape for a curated install. A new node or model then stays unavailable until you list it, instead of appearing because you did not deny it.
- Utility nodes, such as
sticky-noteandpreview, are never removed by omission from an allow list. Only an explicitdenyremoves them. - Admins can change node and model availability at runtime in the admin panel. A saved runtime setting replaces the profile's lists until it is reset to the factory state.
- A node that an admin disables in the admin panel stays usable by admins. A node removed by the profile is removed for admins too. But an admin who enables a profile-removed node again in the admin panel releases it to everyone, so give the admin role only to people you trust with that.
- A disabled model is disabled for admins too.
- A run is checked as the user it runs as. An app or presentation run executes as the person running it. A scheduled or webhook-triggered run executes as the workflow's owner, and an API token or a connected app acts as its own owner. When that owner is an admin, the run can use a node hidden from users, so treat the webhook URL of such a workflow with care.
- An app, a component or a template that contains a hidden node cannot be published: the request fails with
node_not_available. - A change of role reaches these checks within about 5 minutes.
Sign-in methods
auth.methods narrows the sign-in methods on the login page, among email, google and sso. Keep sso in the list only together with auth.ssoLabel, the text of the SSO button.
A list that names only sso also enables a server-side rule: every signed-in account must have been created or linked through single sign-on. Any other session is refused on its first API call with 403 sso_required, so an account registered directly on the sign-in service cannot use the install. Adding email to the list disables the rule. See Single sign-on.
Voice genders
voice.allowedGenders is enforced on the server:
- The voice list and the shared voice library show only voices of the allowed genders.
- A request with a built-in voice of another gender is refused with
voice_not_available. - Every default and fallback voice becomes the first voice of an allowed gender.
- The editor hides the vocal-gender tags of the other genders in the Suno nodes.
The output gender of the voice-creation nodes cannot be known in advance. To remove them, add "nodes":{"deny":["voice-design","voice-remix"]}.
Picker catalogs
For installs that must offer only reviewed picker content, set "catalogs":{"required":true,"factoryPresets":false}.
- With
required: true, the editor offers picker choices only after it receives the complete curated catalog from the server, and offers none if that request fails. New nodes and factory resets use only offered values. - With
factoryPresets: false, the bundled presets are hidden in the editor and the API. Users' own presets stay. - The server also rejects excluded catalog choices in imported or previously saved workflows.
Frequently asked questions
Related
First user and admin
Configuration
Single sign-on
License
Editions
Last updated on
First user and admin
Sign up the first user of a self-hosted Nodaro and promote an admin on the Business edition with one SQL statement, with or without a Supabase dashboard.
Configuration
Every environment variable of a self-hosted Nodaro, grouped by purpose, with its default, whether .env can set it on the compose stack, and what it changes.