memory adapter
In-process Map-backed adapter for development, testing, and prototyping. Zero dependencies, no persistence.
Install
import { MemoryAdapter } from '@gentleduck/iam/adapters/memory'import { MemoryAdapter } from '@gentleduck/iam/adapters/memory'Stores everything in Map instances. Zero dependencies. Data does not survive process restarts — use a database adapter for production.
Basic usage
import { MemoryAdapter } from '@gentleduck/iam/adapters/memory'
import { Engine } from '@gentleduck/iam'
const adapter = new MemoryAdapter({
roles: [
{
id: 'admin',
name: 'Administrator',
permissions: [{ action: '*', resource: '*' }],
},
{
id: 'editor',
name: 'Editor',
permissions: [
{ action: 'read', resource: '*' },
{ action: 'create', resource: 'post' },
{ action: 'update', resource: 'post' },
{ action: 'delete', resource: 'post' },
],
},
{
id: 'viewer',
name: 'Viewer',
permissions: [{ action: 'read', resource: '*' }],
},
],
policies: [
{
id: 'default',
name: 'Default Policy',
algorithm: 'deny-overrides',
rules: [
{
id: 'deny-banned',
effect: 'deny',
priority: 100,
actions: ['*'],
resources: ['*'],
conditions: {
all: [{ field: 'subject.attributes.status', operator: 'eq', value: 'banned' }],
},
},
{
id: 'allow-all',
effect: 'allow',
priority: 1,
actions: ['*'],
resources: ['*'],
conditions: { all: [] },
},
],
},
],
assignments: {
'user-1': ['admin'],
'user-2': ['editor'],
'user-3': ['viewer'],
},
})
const engine = new Engine({ adapter })import { MemoryAdapter } from '@gentleduck/iam/adapters/memory'
import { Engine } from '@gentleduck/iam'
const adapter = new MemoryAdapter({
roles: [
{
id: 'admin',
name: 'Administrator',
permissions: [{ action: '*', resource: '*' }],
},
{
id: 'editor',
name: 'Editor',
permissions: [
{ action: 'read', resource: '*' },
{ action: 'create', resource: 'post' },
{ action: 'update', resource: 'post' },
{ action: 'delete', resource: 'post' },
],
},
{
id: 'viewer',
name: 'Viewer',
permissions: [{ action: 'read', resource: '*' }],
},
],
policies: [
{
id: 'default',
name: 'Default Policy',
algorithm: 'deny-overrides',
rules: [
{
id: 'deny-banned',
effect: 'deny',
priority: 100,
actions: ['*'],
resources: ['*'],
conditions: {
all: [{ field: 'subject.attributes.status', operator: 'eq', value: 'banned' }],
},
},
{
id: 'allow-all',
effect: 'allow',
priority: 1,
actions: ['*'],
resources: ['*'],
conditions: { all: [] },
},
],
},
],
assignments: {
'user-1': ['admin'],
'user-2': ['editor'],
'user-3': ['viewer'],
},
})
const engine = new Engine({ adapter })With subject attributes
Pass initial attributes for ABAC-style conditions.
const adapter = new MemoryAdapter({
roles: [...],
assignments: { 'user-1': ['editor'] },
attributes: {
'user-1': {
department: 'engineering',
level: 3,
verified: true,
},
},
})const adapter = new MemoryAdapter({
roles: [...],
assignments: { 'user-1': ['editor'] },
attributes: {
'user-1': {
department: 'engineering',
level: 3,
verified: true,
},
},
})Constructor options
| Option | Type | Description |
|---|---|---|
roles | Role[] | Initial role definitions |
policies | Policy[] | Initial policy definitions |
assignments | Record<string, string[]> | Map of subject ID to role IDs |
attributes | Record<string, Attributes> | Map of subject ID to attribute objects |
Behavior details
- Duplicate assignments —
assignRolechecks for existing entries and silently skips duplicates. CallingassignRole('user-1', 'editor')twice has no effect the second time. - Attribute merging —
setSubjectAttributesshallow-merges new attributes into existing ones. To remove an attribute, set it tonull. - Scoped vs unscoped roles — stored in the same array.
getSubjectRolesfilters for entries without a scope;getSubjectScopedRolesfilters for entries with a scope.
When to use
- Unit tests — seed known roles and assignments, assert engine behavior
- Development — get started without a database
- Prototyping — explore the policy model before committing to a schema
- CI pipelines — run integration tests without external dependencies
When NOT to use
- Production — process restarts wipe all data
- Multi-instance deploys — each node has its own isolated map
- Persistent state across deployments — every deploy resets