Skip to main content

ACL System

Drumee's ACL system is a bitwise, Linux-inspired permission model that controls access to every backend service. Each service is declared in a JSON configuration file. The server reads this configuration on every request and either dispatches to the service method or returns a 403 — before any service code runs.

How a Request Is Evaluated

Frontend Request

▼ Parse module.method

▼ Session Authentication

▼ Load ACL JSON for module

▼ Get required permission (permission.src)

▼ Check caller's privilege level

├── Privilege >= Required? → Execute Service → Return Result

└── No → 403 Forbidden

The check happens before any service code executes. A service implementation can assume the caller is already authorised — there are no permission checks inside service methods. As a result, service code remains cleaner, execution is safer, and performance is more efficient.

Permission Levels

Drumee uses a numeric (bitwise)privilege model. One bit represents a particular permission. Taken together, the bits represent privileges.

LevelValueDescription
anonymous1No authentication required. Open to any request.
read2Authenticated user with read access.
write4Authenticated user with write access.
admin8Workspace or Organization administrator.
owner16The resource owner. Highest privilege level.

Example: to write into a file, the user must request the permission 4 (0b00100).
If the granted privilege is read only 3 (anonymous + read, i.e 0b00011) the request is rejected.

A caller with the owner privilege (0b11111, i.e 31)) satisfies any permission requirement. A caller with write privilege (0b00111, i.e 7) cannot call services requiring admin permission (8) or owner (16).

Scope Types

The scope field in an ACL entry defines the session context required.

ScopeDescription
hubRequires an active Workspace context. The request must be routed to a Hub endpoint.
domainRequires Organization-level authentication. Used for organisation-wide operations.
publicAuthorize the request with minimal check

The vast majority of services use hub. Some use public only for endpoints explicitly designed for unauthenticated access — there is no audit trail for anonymous calls.

ACL JSON Structure

Each module has a corresponding JSON file in the acl/ directory. The file declares every service the module exposes, its permission requirements, and the implementation path.

{
"services": {
"service_name": {
"scope": "hub",
"permission": {
"src": "write"
},
"log": true
}
},
"modules": {
"private": "service/private/module_name"
}
}

Fields Reference

FieldRequiredDescription
scopeYesAccess context: hub, domain, or public
permission.srcYesMinimum privilege level required
permission.fast_checkNoAdditional runtime check before execution (e.g. user_permission, public-api)
methodNoMaps the service name to a differently named JavaScript method
logNoWhen true, the service call is written to the audit log
preprocNoPre-processing config (e.g. file upload handling: {"checker": "upload"})
modules.privateNoPath to the private (authenticated) service implementation
modules.publicNoPath to the public (unauthenticated) service implementation

Choosing the Right Permission Level

You want...Use
Any authenticated user to call itread
Only users who can edit contentwrite
Only hub administratorsadmin
Only the resource ownerowner
No authentication at allanonymous

Use the minimum necessary privilege. Never use owner when write is sufficient.

The fast_check Mechanism

Some services require a contextual check beyond the static permission level.

ValueBehaviour
user_permissionVerifies the user holds the required privilege on the specific MFS node being accessed — not just on the Hub in general. Used for services that operate on individual files or folders.
public-apiAllows the service to be called without a full authenticated session, for token-based or guest access.
"permission": { "src": "read", "fast_check": "user_permission" }

Method Aliases

When the ACL service name differs from the JavaScript method name, use the method field:

"show_tag_by": { "scope": "hub", "permission": { "src": "owner" }, "method": "tag_get_next" }

The client calls tagcontact.show_tag_by. The server dispatches to the tag_get_next() method.

Service Endpoint Pattern

All backend services are accessed through a single entry point. There are no hard-coded routes.

# Authenticated (hub-scoped) services https://hostname/-/svc/module.method
# Public services (scope: "public") https://hostname/-/api/module.method

Adding a New Service

Two steps only — no route registration, no middleware wiring.

1. Implement the method in the service directory:

// service/private/mymodule.js
async my_action() {
const id = this.input.need('id');
const result = await this.db.await_proc('my_proc', id);
this.output.data(result);
}

2. Declare it in the ACL file:

{
"services": {
"my_action": {
"scope": "hub",
"permission": {
"src": "write"
}
}
},
"modules": {
"private": "service/private/mymodule"
}
}

Security Properties

  • No implicit access. A method with no ACL entry is unreachable from the network, regardless of whether it exists in the service file.
  • Permission enforced before execution. The server checks the ACL before calling any service method — the implementation can assume the caller is already authorised.
  • No bypass via application code. Enforcement happens at the server routing layer before the service class is instantiated. There is no code path that skips the permission check.
  • Audit trail. When "log": true is set, every invocation is recorded with caller identity, timestamp, and result. Enable on all write operations.