Permissions
Policy is deployed with each named schema and denies by default. Read rules decide which facts exist in a principal’s filtered database. Operation rules decide which exact operations the principal may attempt.
Apply policy to the schema
Section titled “Apply policy to the schema”export const ProjectSchema = Ramose.Schema("project-v1", { user: User, task: Task,})
ProjectSchema.applyPolicy( { principal: User.authId, roles: ["member", "admin", "suspended"], }, ({ policy, session }) => { policy.task.read.where(session.hasRole("member")) policy.task.read.where(session.hasRole("admin")) policy.task.read.denyWhere(session.hasRole("suspended")) },)applyPolicy records inert schema-checked policy data. Deployment rejects unknown fields, invalid paths, incompatible targets, and unbounded structures.
Allow an entity
Section titled “Allow an entity”policy.task.read.where(session.hasRole("member"))Multiple allows for the same focus combine with OR. A deny overrides allows at that focus:
policy.task.read.denyWhere(session.hasRole("suspended"))Use the resource and principal
Section titled “Use the resource and principal”policy.task.read.where((task) => task.owner.eq(actor))policy.task.read.where((task) => task.project.members.contains(actor))policy.task.read.where(session.hasRole("admin"))actor is the principal entity resolved through the configured unique field. session.subject is the JWT subject string. Resource relation traversal is bounded and must remain within the schema.
Prefer database-backed membership for rapidly changing access. A token is identity input, not a database selector.
Narrow a field
Section titled “Narrow a field”policy.task.read.where(session.hasRole("member"))policy.task.fields.privateNote.read.where(session.hasRole("admin"))The field rule narrows its parent. A client asking for privateNote receives it only for admins; the field is absent for others. Hidden refs hide the whole edge rather than revealing an inaccessible target id.
Author trait rules
Section titled “Author trait rules”policy.commentable.read.where(session.hasRole("member"))Trait rules apply to the trait fields on readable composers. They do not reopen an entity hidden by its concrete rule. This makes reusable capability policy possible without weakening concrete domains.
Grant exact operations
Section titled “Grant exact operations”policy.task.operations.createTask.where(session.hasRole("member"))policy.task.operations.setDone.where(session.hasRole("member"))policy.task.operations.delete.where(session.hasRole("admin"))Operation expressions are session-only. They can use roles, the subject, and declared claims. They cannot read the target resource. A targeted operation also requires a visible compatible target.
Test observable behavior
Section titled “Test observable behavior”For each role, test visible rows, fields, relations, trait roots, operations, history, counts, ordering, limits, errors, and revocation. Compare hidden and nonexistent cases. The security contract includes what callers cannot infer, not just which successful request returns 200.