Arke
Build

Collections

Creating and managing collections -- the permission boundaries of the Arke network.

Overview

Collections are the fundamental permission boundary in Arke. Every entity belongs to at least one collection, and a user's role in the collection determines what they can do with entities inside it.

Creating a Collection

POST /collections
Authorization: Bearer <token>

{
  "label": "Whaling Archives",
  "description": "Melville's manuscripts and maritime records"
}

Response (201 Created):

{
  "id": "01JCOL...",
  "cid": "bafyrei...",
  "type": "collection",
  "properties": {
    "label": "Whaling Archives",
    "description": "Melville's manuscripts and maritime records",
    "roles": {
      "owner": ["*:view", "*:update", "*:create", "collection:update", "collection:manage"],
      "editor": ["*:view", "*:update", "*:create"],
      "viewer": ["*:view"],
      "public": ["*:view"]
    },
    "_profile_version": "v1"
  },
  "relationships": [
    { "predicate": "public", "peer": "*", "peer_type": "wildcard" },
    { "predicate": "owner", "peer": "01JUSER...", "peer_type": "user" }
  ],
  "ver": 1,
  "created_at": "2025-01-15T12:00:00.000Z",
  "ts": "2025-01-15T12:00:00.000Z",
  "edited_by": { "user_id": "01JUSER...", "method": "manual" }
}

The creating user automatically becomes the owner. If no custom roles are provided, the collection uses default roles (owner, editor, viewer, public).

Optional Fields

FieldTypeDescription
idstringCustom entity ID (generated if not provided)
labelstringRequired. Display name for the collection
descriptionstringCollection description (max 2000 chars)
display_image_urlstringURL for collection display image
rolesobjectCustom role definitions (see below)
propertiesobjectAdditional properties to store
relationshipsarrayInitial relationships
notestringVersion note

Getting a Collection

GET /collections/:id
Authorization: Bearer <token>  # Optional for public collections

Response (200 OK):

{
  "id": "01JCOL...",
  "cid": "bafyrei...",
  "type": "collection",
  "properties": {
    "label": "Whaling Archives",
    "description": "Melville's manuscripts and maritime records",
    "roles": { ... }
  },
  "relationships": [...],
  "ver": 1,
  "created_at": "2025-01-15T12:00:00.000Z",
  "ts": "2025-01-15T12:00:00.000Z",
  "edited_by": { ... }
}

Updating a Collection

PUT /collections/:id
Authorization: Bearer <token>

{
  "expect_tip": "bafyrei...",
  "label": "The Pequod's Archive",
  "description": "Updated description"
}

Response (200 OK):

{
  "id": "01JCOL...",
  "cid": "bafynew...",
  "type": "collection",
  "properties": { ... },
  "relationships": [...],
  "ver": 2,
  "created_at": "2025-01-15T12:00:00.000Z",
  "ts": "2025-01-15T12:30:00.000Z",
  "edited_by": { ... },
  "prev_cid": "bafyrei..."
}

The expect_tip field is required for optimistic concurrency control (CAS). If the current collection version doesn't match, you'll receive a 409 Conflict.

Update Fields

FieldTypeDescription
expect_tipstringRequired. Current CID for CAS check
labelstringUpdated display name
descriptionstringUpdated description
display_image_urlstringUpdated display image URL
propertiesobjectAdditional properties to merge
properties_removeobjectProperties to remove
relationships_addarrayRelationships to add
relationships_removearrayRelationships to remove
notestringVersion note

Note: Roles cannot be updated via this endpoint. Use the role management endpoints below.

Managing Roles

Roles define what actions members can perform. Each collection has a set of role definitions that map role names to action arrays.

Default Roles

RoleActionsDescription
owner*:view, *:update, *:create, collection:update, collection:manageFull control including collection management
editor*:view, *:update, *:createCan modify entities but NOT collection settings
viewer*:viewRead-only access
public*:viewPublic access (required for all collections)

Action Format

Actions follow the resource:verb pattern:

  • entity:view - View entities
  • entity:create - Create entities
  • entity:update - Update entities
  • collection:manage - Manage roles and members
  • *:view - View any resource type (wildcard)
  • file:* - Any action on files (type wildcard)

Add a Role

POST /collections/:id/roles
Authorization: Bearer <token>

{
  "role": "harpooner",
  "actions": ["*:view", "*:update", "*:create"]
}

Response (201 Created):

{
  "id": "01JCOL...",
  "cid": "bafynew...",
  "prev_cid": "bafyold...",
  "roles": {
    "owner": [...],
    "editor": [...],
    "viewer": [...],
    "public": [...],
    "harpooner": ["*:view", "*:update", "*:create"]
  },
  "ver": 3
}

Update a Role

PUT /collections/:id/roles/:role
Authorization: Bearer <token>

{
  "actions": ["*:view", "*:update", "*:create", "entity:delete"]
}

Response (200 OK):

{
  "id": "01JCOL...",
  "cid": "bafynew...",
  "prev_cid": "bafyold...",
  "roles": { ... },
  "ver": 4
}

Delete a Role

DELETE /collections/:id/roles/:role
Authorization: Bearer <token>

Response (200 OK):

{
  "id": "01JCOL...",
  "cid": "bafynew...",
  "prev_cid": "bafyold...",
  "roles": { ... },
  "ver": 5
}

Deleting a role also removes all member assignments for that role. The public role cannot be deleted (required for all collections).

Managing Members

Members are users assigned to roles within a collection. Role assignments are stored as relationships on the collection entity.

List Members

GET /collections/:id/members
Authorization: Bearer <token>

# Optional query parameters:
# ?include_expired=true  - Include expired memberships

Response (200 OK):

{
  "collection_id": "01JCOL...",
  "members": [
    {
      "userId": "01JUSER...",
      "role": "owner",
      "userLabel": "Captain Ahab",
      "granted_at": "2025-01-15T12:00:00.000Z",
      "granted_by": "01JUSER...",
      "is_expired": false
    },
    {
      "userId": "01JUSER2...",
      "role": "editor",
      "userLabel": "Ishmael",
      "expires_at": "2025-02-15T12:00:00.000Z",
      "granted_at": "2025-01-15T12:00:00.000Z",
      "granted_by": "01JUSER...",
      "is_expired": false
    }
  ],
  "groups": [],
  "wildcards": [
    { "role": "public" }
  ]
}

Assign a Member to a Role

POST /collections/:id/members
Authorization: Bearer <token>

{
  "user_id": "01JUSER...",
  "role": "editor"
}

Response (201 Created):

{
  "id": "01JCOL...",
  "cid": "bafynew...",
  "prev_cid": "bafyold...",
  "member_added": {
    "user_id": "01JUSER...",
    "role": "editor",
    "granted_at": "2025-01-15T12:00:00.000Z",
    "granted_by": "01JACTOR..."
  },
  "ver": 6
}

Temporary Access

You can grant time-limited access using the expires_in field (seconds):

POST /collections/:id/members
Authorization: Bearer <token>

{
  "user_id": "01JUSER...",
  "role": "editor",
  "expires_in": 86400
}

Response (201 Created):

{
  "id": "01JCOL...",
  "cid": "bafynew...",
  "prev_cid": "bafyold...",
  "member_added": {
    "user_id": "01JUSER...",
    "role": "editor",
    "expires_at": "2025-01-16T12:00:00.000Z",
    "granted_at": "2025-01-15T12:00:00.000Z",
    "granted_by": "01JACTOR..."
  },
  "ver": 6
}

Remove a Member from a Role

DELETE /collections/:id/members/:userId?role=editor
Authorization: Bearer <token>

The role query parameter is required to specify which role assignment to remove.

Response (200 OK):

{
  "id": "01JCOL...",
  "cid": "bafynew...",
  "prev_cid": "bafyold...",
  "member_removed": {
    "user_id": "01JUSER...",
    "role": "editor"
  },
  "ver": 7
}

Setting a Root Entity

Collections can have a "root" entity that serves as the entry point for the collection's content hierarchy. This is useful for folder structures or document trees.

PUT /collections/:id/root
Authorization: Bearer <token>

{
  "expect_tip": "bafyrei...",
  "entity_id": "01JFOLDER..."
}

Prerequisites: The entity must already have a collection relationship pointing to this collection (typically set during entity creation via the collection field).

Response (200 OK):

{
  "id": "01JCOL...",
  "cid": "bafynew...",
  "type": "collection",
  "properties": { ... },
  "relationships": [
    ...,
    { "predicate": "root", "peer": "01JFOLDER...", "peer_type": "folder" }
  ],
  "ver": 8,
  "created_at": "...",
  "ts": "...",
  "edited_by": { ... },
  "prev_cid": "bafyrei...",
  "root_entity_id": "01JFOLDER..."
}

Listing Collection Entities

Retrieve all entities belonging to a collection via the graph database.

GET /collections/:id/entities
Authorization: Bearer <token>

# Query parameters:
# ?type=document      - Filter by entity type
# ?limit=100          - Max entities (default: 1000, max: 10000)
# ?offset=0           - Pagination offset
# ?expand=preview     - Include lightweight previews
# ?expand=full        - Include full entity manifests (max 100 entities)

Response (200 OK):

{
  "collection_id": "01JCOL...",
  "entities": [
    {
      "pi": "01JDOC...",
      "type": "document",
      "label": "Chapter 1: Loomings",
      "created_at": "2025-01-15T12:00:00.000Z",
      "updated_at": "2025-01-15T14:00:00.000Z"
    }
  ],
  "pagination": {
    "offset": 0,
    "limit": 100,
    "count": 1,
    "has_more": false
  }
}

Expansion Modes

By default, returns lightweight summaries from GraphDB.

  • ?expand=preview: Adds preview field with fresh lightweight data (label, truncated description/text, timestamps). ~5-10KB per entity.
  • ?expand=full: Adds entity field with complete manifest (all properties, relationships, version history). ~20-50KB per entity. Limited to 100 entities.

Finding Entities by Label

Collections maintain a local index of entity labels for fast lookup. These endpoints query the collection's index directly (not the graph database), providing sub-10ms response times.

Exact Lookup

Find entities with an exact label match (case-insensitive):

GET /collections/:id/entities/lookup?label=Captain%20Ahab
Authorization: Bearer <token>

# Query parameters:
# ?label=...   - Required. Exact label to match
# ?type=...    - Filter by entity type
# ?limit=10    - Max results (default: 10)

Response (200 OK):

{
  "entities": [
    {
      "pi": "01JCHAR...",
      "type": "character",
      "label": "Captain Ahab",
      "cid": "bafyrei...",
      "updated_at": "2025-01-15T14:00:00.000Z"
    }
  ],
  "count": 1
}

Use this when you know the exact entity label.

Find entities with labels containing a substring (case-insensitive):

GET /collections/:id/entities/search?q=ahab
Authorization: Bearer <token>

# Query parameters:
# ?q=...       - Required. Search term (substring match)
# ?type=...    - Filter by entity type
# ?limit=20    - Max results (default: 20)

Response (200 OK):

{
  "entities": [
    {
      "pi": "01JCHAR...",
      "type": "character",
      "label": "Captain Ahab",
      "cid": "bafyrei...",
      "updated_at": "2025-01-15T14:00:00.000Z"
    },
    {
      "pi": "01JCHAP...",
      "type": "chapter",
      "label": "Chapter 36: Ahab's Leg",
      "cid": "bafyrei...",
      "updated_at": "2025-01-15T14:00:00.000Z"
    }
  ],
  "count": 2
}

Use this for partial matches or keyword discovery.

Which Endpoint to Use

ScenarioEndpointExample
Know exact label/entities/lookup"Get entity labeled 'Captain Ahab'"
Partial match/entities/search"Find entities with 'ahab' in label"
Browse all entities/entities"List all entities in collection"
Full-text content searchSemantic search API"Find content about whale hunting"

The lookup and search endpoints query the collection's local index. For full-text semantic search across entity content, use the Search API.

Permission Summary

EndpointRequired Action
POST /collectionscollection:create
GET /collections/:idcollection:view
PUT /collections/:idcollection:update
POST /collections/:id/rolescollection:manage
PUT /collections/:id/roles/:rolecollection:manage
DELETE /collections/:id/roles/:rolecollection:manage
GET /collections/:id/memberscollection:view
POST /collections/:id/memberscollection:manage
DELETE /collections/:id/members/:userIdcollection:manage
PUT /collections/:id/rootcollection:update
GET /collections/:id/entitiescollection:view
GET /collections/:id/entities/lookupcollection:view
GET /collections/:id/entities/searchcollection:view

Public vs Private

All collections have a public role with minimum *:view permissions. This is a platform requirement. The public wildcard relationship ({ predicate: "public", peer: "*", peer_type: "wildcard" }) makes the collection discoverable by anyone on the network.

To restrict visibility, you can remove the public wildcard relationship or customize the public role's actions (while maintaining minimum platform requirements).

On this page