PageSpace uses a direct permission model built on three concepts: drive ownership, drive membership, and page-level permissions.
Every drive has a single owner with irrevocable full access. The owner can:
Users can be added to drives with one of three roles:
| Role | Capabilities |
|---|---|
| OWNER | Full access to everything in the drive |
| ADMIN | Drive management, member management |
| MEMBER | Basic drive visibility, page access based on page permissions |
Direct user-to-page permissions with boolean flags:
| Flag | Description |
|---|---|
canView | Read page content |
canEdit | Modify page content |
canShare | Manage page permissions (share with other users) |
canDelete | Move page to trash |
Each permission is a direct relationship — User X has specific permissions on Page Y. There is no permission inheritance from parent pages.
When a user requests access to a page:
User requests Page Y
└→ Is user the drive owner?
├→ Yes → Full access (view, edit, share, delete)
└→ No → Check pagePermissions table
├→ Record found → Return { canView, canEdit, canShare, canDelete }
└→ No record → Access denied
Drive A is owned by Alice. It contains Folder X, which contains Document Y.
canView: true, canEdit: true on Document Y → Can view and editPOST /api/pages/{pageId}/permissions
{
"userId": "user-bob-123",
"canView": true,
"canEdit": true,
"canShare": false,
"canDelete": false
}
Requires: Drive owner or canShare permission on the page.
DELETE /api/pages/{pageId}/permissions
{
"userId": "user-bob-123"
}
GET /api/pages/{pageId}/permissions/check
// Returns: { canView: true, canEdit: true, canShare: false, canDelete: false }
Drive owners can view a hierarchical permissions overview:
GET /api/drives/{driveId}/permissions-tree?userId=user-bob-123
// Returns all pages with Bob's permission status
Permissions are cached using a two-tier strategy:
| Tier | Storage | TTL | Max Entries |
|---|---|---|---|
| L1 | In-memory Map | 60s | 1,000 |
| L2 | Redis | 60s | Unlimited |
| Scenario | Behavior |
|---|---|
| Cache miss | Falls through to database query, result cached |
| Redis unavailable | L1 cache still operates, no denial of service |
| Invalidation failure | Logged, stale entry expires within 60s TTL |
| Database error | Returns null (access denied) |
Cache failures never result in unauthorized access — the system always falls through to the database on error.
| Method | Route | Description |
|---|---|---|
| GET | /api/pages/{id}/permissions | List all permissions for a page |
| POST | /api/pages/{id}/permissions | Grant or update permissions |
| DELETE | /api/pages/{id}/permissions | Revoke user's permissions |
| GET | /api/pages/{id}/permissions/check | Check current user's permissions |
| GET | /api/drives/{id}/permissions-tree | Hierarchical permissions view |
| POST | /api/permissions/batch | Batch permission updates |
Search docs, blog posts, and more.