Skip to main content

From Solo Dev to Team: How Filess Models Multi-Tenancy with Organizations, Namespaces, and RBAC

· 7 min read
Filess Team
Database Experts

You start as a solo developer. One account, one database, no access control needed.

Then you hire a backend developer. You want them to see the production database metrics but not be able to delete anything. Then a contractor joins for three months — you want them to access the staging namespace only. Then your company gets acquired and the acquirer wants their DevOps team to manage infrastructure without touching billing.

Most database platforms weren't designed for this. You end up sharing root credentials via Slack DMs and hoping nobody does something irreversible.

Filess was designed from day one for teams that grow. Here's how the access model works.

The Three-Layer Model

Access in Filess is organized in three layers:

Organization
└── Namespace (prod / staging / ci / team-a / team-b)
└── Resources (databases, tunnels, hosting projects)

Organizations

An Organization is the top-level billing and access unit. It represents a company, a team, or a project. All resources — databases, tunnels, hosting deployments — belong to an organization.

Key organization-level permissions:

  • organization:view — Can see the organization exists.
  • organization:update — Can edit organization settings.
  • organization:billing:view — Can see billing and invoices.
  • organization:invite:create — Can invite new members.
  • organization:transfer_ownership — Can change who owns the organization.

Namespaces

A Namespace is a logical partition within an organization. You might have:

  • prod — Production databases, locked down. Only senior engineers can touch.
  • staging — Staging databases. Full team access.
  • ci — Ephemeral databases created and destroyed by CI pipelines.
  • team-frontend — Backend databases that the frontend team needs read access to.

Namespaces let you implement environment isolation without separate accounts. The same team member can have write access in staging and read-only access in prod — from a single login.

Namespace-level permissions:

  • namespace:create_in_organization — Can create new namespaces.
  • namespace:view_in_organization — Can see a namespace.
  • namespace:update_in_organization — Can rename/modify a namespace.
  • namespace:delete_in_organization — Can delete a namespace and all its resources.

Resources

Resources (databases, tunnels, SSH keys, IP allowlists, hosting projects) live inside namespaces. Each resource type has its own granular permission set.


Roles and Groups

Rather than assigning permissions directly to users (which doesn't scale), Filess uses Roles and Groups.

Roles

A Role is a named set of permissions. You create roles that match your team structure:

Example: db-reader

database:view_in_namespace
metrics:view_in_database
log:view_in_database
backup:view_in_database

Example: db-operator

database:view_in_namespace
database:update_in_namespace
metrics:view_in_database
log:view_in_database
backup:create_in_database
backup:view_in_database
backup:restore_in_database
maintenance_window:view_in_database
maintenance_window:create_in_database

Example: db-admin

database:create_in_namespace
database:view_in_namespace
database:update_in_namespace
database:delete_in_namespace
... (all database permissions)
backup:*
maintenance_window:*
alert_rule:*

Roles are defined at the organization level and can be applied to any namespace within that organization.

Groups

A Group is a collection of users. Instead of assigning roles to individual users, you assign roles to groups, then add users to groups.

Why groups?

  • When an employee joins, add them to the right group and they inherit all permissions immediately.
  • When an employee leaves, remove them from the group and all access is revoked in one operation.
  • When you onboard a contractor, create a time-limited group with restricted access.
Group: "backend-engineers"
Members: alice, bob, charlie
Namespaces: prod (role: db-operator), staging (role: db-admin)

Group: "frontend-engineers"
Members: diana, erin
Namespaces: staging (role: db-reader)

Group: "ci-bots"
Members: github-actions-bot
Namespaces: ci (role: db-admin)

The Full Permission Matrix

Here's every permission in the system, organized by resource type:

Databases

PermissionWhat it allows
database:create_in_namespaceCreate new databases
database:view_in_namespaceSee database details and connection info
database:update_in_namespaceModify database configuration
database:delete_in_namespaceDelete databases
metrics:view_in_databaseSee Prometheus metrics and charts
log:view_in_databaseAccess database logs

Backups

PermissionWhat it allows
backup:create_in_databaseTrigger manual backups
backup:view_in_databaseSee backup history
backup:restore_in_databaseRestore from backup (including PITR)

Maintenance Windows

PermissionWhat it allows
maintenance_window:view_in_databaseSee scheduled maintenance
maintenance_window:create_in_databaseSchedule maintenance windows
maintenance_window:update_in_databaseModify existing windows
maintenance_window:delete_in_databaseCancel scheduled maintenance
maintenance_window:change_scheduled_dateReschedule maintenance

Alert Rules

PermissionWhat it allows
alert_rule:view_in_databaseSee alert configuration
alert_rule:create_in_databaseCreate new alert rules
alert_rule:update_in_databaseModify alert rules
alert_rule:delete_in_databaseRemove alert rules

Tunnels

PermissionWhat it allows
tunnel:view_in_namespaceSee tunnel details
tunnel:create_in_namespaceCreate tunnels
tunnel:update_in_namespaceModify tunnel configuration
tunnel:delete_in_namespaceDelete tunnels

SSH Keys

PermissionWhat it allows
sshkey:create_in_organizationAdd SSH keys
sshkey:view_in_organizationList SSH keys
sshkey:update_in_organizationModify SSH keys
sshkey:delete_in_organizationRemove SSH keys

IP Allowlists

PermissionWhat it allows
ip:create_in_namespaceAdd IP allowlist entries
ip:view_in_namespaceSee IP allowlists
ip:update_in_namespaceModify allowlist entries
ip:delete_in_namespaceRemove allowlist entries

Tailscale

PermissionWhat it allows
tailscale:view_in_organizationSee Tailscale configurations
tailscale:create_in_organizationAdd Tailscale configs
tailscale:delete_in_organizationRemove Tailscale configs

A Real Scenario: Fintech Startup

You're building a fintech product. Here's how you'd structure Filess access:

Organization: acme-fintech

Namespaces:
- prod (Production databases)
- staging (Staging databases)
- dev (Development databases)
- ci (CI ephemeral databases)

Groups:
- sre-team
prod: db-admin
staging: db-admin
dev: db-admin
ci: db-admin

- backend-team
prod: db-reader ← Can see metrics, not modify
staging: db-operator ← Can manage backups, not delete
dev: db-admin ← Full control
ci: db-admin

- auditors
prod: db-reader ← Read-only, can see audit logs
staging: (no access)
dev: (no access)

- github-actions
ci: db-admin ← Create/delete ephemeral DBs
staging: db-operator ← Run migrations on staging

A new backend engineer joins → add to backend-team. They immediately have the right access to every environment.

A compliance audit starts → add the auditor to auditors. They can see production metrics and logs without being able to modify anything.

A contractor leaves → remove from their group. All access revoked instantly, no password rotation needed.


Audit Logs

Every administrative action is logged:

2026-04-01 09:14 UTC  [email protected]  created database "payments-prod"  namespace=prod
2026-04-01 09:45 UTC [email protected] created backup database="payments-prod"
2026-04-02 14:22 UTC [email protected] updated alert rule "High CPU" database="payments-prod"
2026-04-03 03:17 UTC github-actions created database "pr-452-test" namespace=ci
2026-04-03 03:31 UTC github-actions deleted database "pr-452-test" namespace=ci

The audit log is viewable by anyone with audit_log:view_in_organization permission. Export it for compliance purposes.


The Security Model Under the Hood

Permission checks happen server-side on every API request. The middleware:

  1. Extracts the authenticated user from the session or JWT token.
  2. Determines the target resource and what action is being performed.
  3. Looks up the user's group memberships.
  4. For each group, checks if the group has a role assigned to the relevant namespace.
  5. Checks if that role includes the required permission.
  6. If any path returns true, the request proceeds. Otherwise, 403 Forbidden.
// From the codebase — every protected controller wraps this check:
if (!(await doesUserHavePermission(
req.user,
[ActionTypes.DATABASE_DELETE_IN_NAMESPACE],
{ organizationId: org.id, namespaceId: namespace.id }
))) {
throw new FilessError(`You do not have permission to delete this database`, 403, true);
}

There's no way to bypass this check by passing different parameters in the request body — the resource ownership is always verified independently.


Starting Simple

You don't have to use all of this on day one. When you're a solo developer:

  • You have one organization.
  • You have one namespace.
  • You're the owner — you have all permissions.

You can add team members, create namespaces, define roles, and build groups incrementally as your team grows. The system scales with you rather than forcing you to over-engineer from the start.


What's Next

The RBAC system is actively being extended. On the roadmap:

  • Time-limited group memberships (perfect for contractors).
  • Per-database access grants (in addition to per-namespace).
  • API token scoping — limit a token to specific namespaces or operations.

Invite your team to Filess →