Schema
CREATE TABLE roles (
name TEXT PRIMARY KEY,
title TEXT NOT NULL DEFAULT '',
soul TEXT NOT NULL DEFAULT '',
tools TEXT NOT NULL DEFAULT '[]', -- JSON array of MCP tool names
builtin INTEGER NOT NULL DEFAULT 0,
can_grant_roles INTEGER NOT NULL DEFAULT 0,
created_by TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
Semantics
toolsis the authority. It is read by bothtools/list(visibility) andtools/call(enforcement); there is no second source of truth.soulis advisory. It becomes the bot'sSOUL.md, shaping voice and intent. A prompt can argue with a soul; it cannot argue with a missing tool.builtinrows are seeded once withINSERT OR IGNORE, so an operator edit is preserved. They cannot be deleted.can_grant_rolesis what makes a role able to define other roles. Only the seededmasterhas it, and it is the checkroles.createperforms — not a name comparison.
Invariants
- A role in use cannot be deleted (
users.rolewould point at nothing). - An unknown role resolves to no tools rather than to a default set.
users.roleis validated against this table on create and on update, so custom roles work from the CLI and the UI, not only from MCP.
Rendered from nx-master/data-model/roles.md · OKF bundle · regenerate with npm run render