Skip to main content

Get started

Folder management

Folder management is used to organize documents in a hierarchical structure. A folder is a group of documents which can be shared with users or teams. A document can be a single 2D grid note (board), a notebook, or a PDF.

Folder model

NameDescription
_idFolder ID
nameFolder name
projectProject ID
parentParent folder ID
childrensArray of { item: ID, itemType: string }
usersArray of User ID
teamsArray of Team ID
inheritPermissionsWhether to inherit permissions from parent (default: true)
createdAtFolder created date
updateDateLast Update Date

Supported document types

The childrens array can contain different types of documents:

TypeDescription
FolderSubfolder for organizing content
Board2D grid note/board
NotebookNotebook document
AssetUploaded file (PDF, images, etc.)
UserInputFormStatic output / user input form

Each document type may have its own specific permission settings in addition to folder-level permissions.

Permission inheritance

Folders can be configured to either inherit permissions from their parent folder or maintain their own independent permissions:

  1. Inheritance enabled (default):

    • Folder automatically inherits user and team permissions from parent
    • Changes to parent folder permissions propagate to child folders
    • Moving folder to new parent updates permissions unless explicitly kept
  2. Independent permissions:

    • Set inheritPermissions: false to maintain separate permissions
    • Folder keeps its own user and team lists
    • Parent folder permission changes don't affect this folder
    • Moving folder preserves its permission settings

Nested Permission Resolution

When checking if a user has access to a document or folder, the system follows these rules:

  1. Document Access Check:

    Folder Structure:

    Folder A
    └── Folder B
    └── Folder C
    └── Document D

    Access Path:
    Document D → Folder C → Folder B → Folder A

    The system checks permissions in this order:

    1. Document's own permissions (if it has independent permissions)
    2. Immediate parent folder (Folder C)
    3. Parent's parent folder (Folder B)
    4. Root parent folder (Folder A)
  2. Permission Inheritance Chain:

    • If any folder in the chain has inheritPermissions: false:
      • Only check that folder's permissions
      • Stop checking higher-level folders
    • Example:
      Folder A (users: [1,2,3])
      └── Folder B (inheritPermissions: true)
      └── Folder C (inheritPermissions: false, users: [4,5])
      └── Document D
      • Document D is only accessible by users [4,5]
      • Even though it's in Folder A, users [1,2,3] cannot access it
      • Folder C breaks the inheritance chain
  3. Moving Folders and Permissions:

    Initial Structure:

    Folder X (users: [1,2])
    └── Folder A
    └── Folder B (inheritPermissions: true)
    └── Document 1

    Folder Y (users: [3,4])
    └── Folder C
    └── Folder D (inheritPermissions: true)

    After moving Folder B to Folder D:

    Folder X (users: [1,2])
    └── Folder A

    Folder Y (users: [3,4])
    └── Folder C
    └── Folder D (inheritPermissions: true)
    └── Folder B (inheritPermissions: true)
    └── Document 1

    Permission handling during move:

    1. Default Behavior (keepPermissions: false):

      • Folder B inherits permissions from new parent (Folder D)
      • Document 1 now accessible by users [3,4]
      • Original permissions from Folder A [1,2] are no longer applied
    2. Keep Original (keepPermissions: true):

      • Folder B keeps its inherited permissions from Folder A
      • Document 1 still accessible by users [1,2]
      • New parent permissions [3,4] not applied
    3. Independent Permissions:

      • If Folder B has inheritPermissions: false
      • Its permissions remain unchanged during move
      • Not affected by either old or new parent

Permission Check Algorithm


async function hasAccess(userId, itemId, userTeams = []) {
const item = await findItem(itemId);
if (!item) return false;

// Check if user/team has direct access to item
if (item.users?.includes(userId)) return true;
if (item.teams?.some(teamId => userTeams.includes(teamId))) return true;

// If item doesn't inherit and no direct access, deny
if (!item.inheritPermissions) return false;

// Follow permission chain up through parent folders
let currentFolder = await findFolder(item.parent);
while (currentFolder) {
// Check current folder permissions
if (currentFolder.users?.includes(userId)) return true;
if (currentFolder.teams?.some(teamId => userTeams.includes(teamId)))
return true;

// Stop if current folder doesn't inherit
if (!currentFolder.inheritPermissions) break;

// Move up to parent
currentFolder = await findFolder(currentFolder.parent);
}

// No access found in permission chain
return false;
}

Example permission chain scenarios:

Scenario 1: Basic Inheritance with Users
Folder A (users: [1,2])
└── Folder B (inheritPermissions: true)
└── Document X
Result: User 1 has access to Document X

Scenario 2: Basic Inheritance with Users and Teams
Folder A (users: [1], teams: ['team1'])
└── Folder B (inheritPermissions: true)
└── Folder C (inheritPermissions: true)
└── Document W
Result: User 1 and team1 members have access to Document W (full inheritance chain)

Scenario 3: Break in Inheritance Chain
Folder A (users: [1,2])
└── Folder B (inheritPermissions: false, users: [3])
└── Document Y
Result: User 1 cannot access Document Y

Scenario 4: Team Access
Folder A (teams: ['team1'])
└── Folder B (inheritPermissions: true)
└── Document Z
Result: Users in team1 have access to Document Z

Scenario 5: Mixed Inheritance
Folder A (users: [1], teams: ['team1'])
└── Folder B (inheritPermissions: true)
└── Folder C (inheritPermissions: false, users: [2])
└── Document W
Result: Only user 2 has access to Document W (Folder C breaks inheritance)