Skip to main content

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

OptionTypeDescription
rolesRole[]Initial role definitions
policiesPolicy[]Initial policy definitions
assignmentsRecord<string, string[]>Map of subject ID to role IDs
attributesRecord<string, Attributes>Map of subject ID to attribute objects

Behavior details

  • Duplicate assignmentsassignRole checks for existing entries and silently skips duplicates. Calling assignRole('user-1', 'editor') twice has no effect the second time.
  • Attribute mergingsetSubjectAttributes shallow-merges new attributes into existing ones. To remove an attribute, set it to null.
  • Scoped vs unscoped roles — stored in the same array. getSubjectRoles filters for entries without a scope; getSubjectScopedRoles filters 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

For production, switch to Prisma, Drizzle, or Redis.