# High level definition
General append strategy is folder based
📁 Parent folder / dossier <- Owner (full access)
📁 Sub folder / dossier <- Owner (full access) + Luca (Read access)
🧾 Document <- Owner (full access) + Luca (Read access)
🧾 Document <- Owner (full access) + Luca (Read access) + Oliver (Read access)
🧾 Document <- Owner (full access)
By default, permissions shall be assigned to the folders, documents inherit permissions from their parent folder. Sub folder by default inherits permissions from the direct parent folder. Document by default inherits permissions from the direct parent folder. Subfolders can add permissions in addition to those inherited from the direct parent folder. Document can add permissions in addition to those inherited from the direct parent folder
Internal dossier structure
🏢 Tenant - Organization A
🗀 Root Folder
🗀 User A Folder
🗀 Session - 1 Folder
🗀 Session - 2 Folder
📁 Juris Case M. Smith
📁 Evidence
🗀 User B Folder
🏢 Tenant – Organization B
🗀 Root Folder
...
🗀 Hidden Folder (type: root | user | session) – Entirely managed by the system.
📁 User visible Folder (type: visible) – Managed by users.
Folder Lifetime:
Root Folder (type=root) ->
Lives as long as the Tenant exist
User Folder (type=user) ->
Lives as long as the User exist
Session Folder (type=session) ->
Lives as long as the Session exist
Juris Case Folder (type=visible) ->
Lives as long as the user doesn't delete it
Evidence (type=visible) ->
Lives as long as the user doesn't delete it
Override feature
Allow breaking inheritance when needed: e.g. confidential document inside a shared folder
📁 Parent folder / dossier
📁 Sub folder / dossier
🧾 Document
🧾🔐 Document
📁🔐 Protected folder
🧾 Document (only inherits permissions from the direct parent folder)
🔐Protected Folder or Document
Ignore all previous permissions and grant access only to the user who created the document or folder, along with the document or folder own permissions.
"Protected" means:
- Inheritance from all ancestors is stopped
- Only the resource's own permissions apply
- Document Super admin bypass still applies
- The acting group must hold SHARING permission over the resource to protect / unprotect it.
PERMISSIONS
| Permission | Documents | Folders | Meaning |
|---|---|---|---|
| READ | X | X | View/open document or folder |
| SHARING | X | X | Manage permissions over the dossier and document (sharing) |
| DELETE | X | X | Delete document or folder |
| ADD | X | Permission to add documents in the folder. Permission to create a new folder | |
| UPDATE | X | X | Update folder information (e.g., dossier's name). Note: No need for UPDATE on a document unless versioning is implemented |
| OWNERSHIP | (X) | X | Modify the owner of a folder. Note: Moves the folder into a folder belonging to the user performing the action (default: user's hidden folder). To change ownership of a document or folder, the user must move it to one of their folders, breaking inheritance from the previous location |
ROLES
| Role | READ | SHARING | DELETE | ADD | UPDATE | OWNERSHIP |
|---|---|---|---|---|---|---|
| SUPER ADMIN | X | X | X | X | X | X |
| OWNER | X | X | X | X | X | X |
| ADMIN | X | X | X | X | X | |
| MANAGER | X | X | X | X | ||
| VIEWER | X |
The DOCUMENT SUPER ADMIN role grants access to all documents and folders in the tenant, whether the entity is protected or not.
The tenant group is a standard group identified by holding the system tenant-admin capability, connected through the existing (Group)-[:HAS_ROLE]->(:UserRole)-[:HAS_CAPABILITY]->(:UserCapability:SystemAcl {id: 'tenant-admin'}) chain. There is exactly one tenant group per tenant, created at bootstrap. It has no [:GRANT:<PERMISSION>] relationships on any resource — it bypasses the permission system entirely based on the capability lookup.
Note on role types: The Roles table above describes ACL Roles — in-memory abstractions that bundle permissions (OWNER, ADMIN, MANAGER, etc.). These are never stored as Neo4j nodes. The
(Group)-[:HAS_ROLE]->(:UserRole)-[:HAS_CAPABILITY]->(:Capability)graph pattern describes a separate System/Tenant Role mechanism used exclusively to identify special system groups (e.g. the tenant group). The two role concepts are independent.
ACL Roles are assigned to groups.
Deletion & Restore
- Use soft delete first for folders and documents
- As soon as deletion is triggered, the item becomes immediately inaccessible.
- Children of a deleted folder become inaccessible too.
- Keep a short recovery window if your business needs it. (for DOCUMENT SUPER ADMIN (and OWNERSHIP ??))
- Run the cleanup asynchronously once the recovery window has passed. (system cron task)
Exception — Session resources are directly purged (no retention): When a Session ends, its Session Folder and all resources it contains (documents and sub-folders) are immediately and permanently deleted. No recovery window applies.
Restrictions
What about cross tenant access?
No access across tenants under any condition. A user must either use a separate account to access another tenant or exist as a duplicated user within that other tenant.
Can there be multiple owners?
No
Can ownership be transferred without moving the entity?
No, ownership cannot be transferred without moving the entity. (An exception may apply for folders located in the Root Folder, since they do not belong to a specific user but to the organization. Only a document super admin create these entities)
Technical implementation
Traceability - events that should be logged
| Event | Definition |
|---|---|
| Resource created | |
| Resource moved | |
| Resource renamed / metadata updated | |
| Resource deleted | |
| Resource restored | |
| Permission granted | |
| Permission revoked | |
| Resource marked protected | |
| Resource unprotected | |
| Ownership changed | |
| Sharing action performed | |
| Access denied | |
| Access granted | |
| Super admin access used | |
| Group membership changed | |
| User removed from tenant |
Resources In Neo4J DB
Entity Relationship Model (Transcribed from Diagram)
graph TD
USER([👤 USER])
GROUP([👥 GROUP])
DOS([📁 DOS\nFolder / Dossier])
DOC([🧾 DOC\nDocument])
USER -- ":MEMBER_OF" --> GROUP
GROUP -- ":OWNS" --> DOS
GROUP -- ":GRANT:<PERMISSION>" --> DOS
GROUP -- ":GRANT:<PERMISSION>" --> DOC
GROUP -- ":OWNS" --> DOC
DOS -- ":CONTAINS" --> DOS
DOS -- ":CONTAINS" --> DOC
Entities
| Entity | Description |
|---|---|
| USER | |
| GROUP | |
| DOS | Folder / Dossier |
| DOC | Document |
| SESSION | User session; lifecycle-binds a hidden session dossier (type=session) |
Relationships
| From | Relationship | To | Description |
|---|---|---|---|
| USER | :MEMBER_OF |
GROUP | The user is a member of a group. |
| GROUP | :OWNS |
DOS | The group owns the dossier (folder). |
| GROUP | :GRANT:<PERMISSION> |
DOS | The group grants permissions on the dossier. |
| GROUP | :GRANT:<PERMISSION> |
DOC | The group grants permissions on the document. |
| GROUP | :OWNS |
DOC | The group owns the document. |
| DOS | :CONTAINS |
DOS | A dossier can contain another dossier (folder hierarchy). |
| DOS | :CONTAINS |
DOC | A dossier directly contains a document. |
| USER | :HAS_SESSION |
SESSION | The user has an active session. |
| SESSION | :HAS_DOSSIER |
DOS | Lifecycle binding: session deletion triggers immediate purge of the linked dossier (type=session) and all its contents. |
Permission List
| Permission |
|---|
| READ |
| SHARING |
| DELETE |
| ADD |
| UPDATE |
| OWNERSHIP |
Nodes definition
Authorization namespace
Internal authorization nodes are explicitly partitioned with the SystemAcl label:
User:SystemAcl, Group:SystemAcl, UserRole:SystemAcl, and UserCapability:SystemAcl. ACL
membership, ownership, grant, and tenant-admin queries must match these composite labels.
Imported diagram or ontology nodes may also use the generic Group label, but their
Group:Entity nodes are intentionally not authorization groups and must not receive SystemAcl.
| Node | Attributes | Definition |
|---|---|---|
| User | Id, email, fullname, username | The user's hidden system group is reached via (User)-[:MEMBER_OF]->(Group {isUserGroup:true}); the user's hidden dossier via (Group)-[:OWNS]->(Dossier {type:'user'}). |
| Group | Id, name, isUserGroup | isUserGroup = true for hidden single-user groups, managed entirely by the system. The tenant group is identified by holding the tenant-admin capability via (Group:SystemAcl)-[:HAS_ROLE]->(:UserRole:SystemAcl)-[:HAS_CAPABILITY]->(:UserCapability:SystemAcl {id: 'tenant-admin'}) — no property flag on the node. |
| Capability | Id, localized names, hidden | Application capabilities use hidden = true for system-only capabilities such as tenant-admin; hidden capabilities remain persisted and assigned but are omitted from roles management UI. |
| Document | Id, name, isProtected, isDeleted, createdAt, updatedAt, deletedAt, deletedBy | Ownership is tracked via the (Group)-[:OWNS]->(Document) relationship. Parent dossier is resolved via the (Dossier)-[:CONTAINS]->(Document) relationship. |
| Dossier | Id, name, type (root | user | session | visible), isProtected, isDeleted, createdAt, updatedAt, deletedAt, deletedBy | root: hidden root dossier of a tenant — no [:OWNS] relationship (sole exception to group-ownership). user: hidden dossier of a user. session: hidden session dossier. visible: user-created dossier. Parent dossier is resolved via the (Dossier)-[:CONTAINS]->(Dossier) relationship (null for the root dossier). |
| Session | Id | Links a user to their session dossier (type=session) via the (User)-[:HAS_SESSION]->(Session) relationship. Deletion triggers immediate purge of the dossier and all its contents. |
Nodes relationship
| Relationship | Definition | From | To | Note |
|---|---|---|---|---|
| GRANT | grantedAt, grantedBy | Group | Document, Dossier | A group has been given access to a resource. Permission is encoded in the relationship type — one typed relationship per permission (e.g. [:GRANT:READ], [:GRANT:SHARING]). Role (e.g. Manager) is a higher-level abstraction; it is not stored in the graph. Users are never granted directly. |
| OWNS | Group | Document, Dossier | A group is responsible for the resource. Mandatory 1(Group) → 1(Resource). Users own resources via their hidden system group. Exception: the root dossier (type=root) has no owner group — no [:OWNS] relationship points to it. It is the sole exception to this rule. |
|
| CONTAINS | Dossier | Document, Dossier | A dossier contains a document or a sub-dossier. | |
| MEMBER_OF | User | Group | ||
| HAS_SESSION | User | Session | A user has an active session. | |
| HAS_DOSSIER | Session | Dossier | Lifecycle binding: session deletion triggers immediate purge of the dossier (type=session) and all its contents. |
Vector search – search first
Graph DB = Source of truth for permission Vector DB = Store embeddings + document IDs Step 1: Run vector search to get candidate documents
Step 2: Filter candidates using access control
Step 3: Return only accessible document
What if top 100 results contains only unauthorized documents? → Fetch next batch
thalabus current nodes and structure
Remember that the document node does not directly contain the embedding value.
Current model:
Document
├── Chapter(s) ← embeddings here
│ └── Paragraph(s) ← embeddings here
└── Summary ← embedding here
Knowledge Base (Ontology) ACL
Knowledge Base resources (Ontologies and their children) are first-class ACL resources, integrated
into the same permission system as Documents and Dossiers. No separate ACL service is introduced —
all checks go through the existing IAclService. The unifying architectural marker is IAclResource,
a common interface implemented by INeo4jOntology, INeo4jDocument, and INeo4jDossier.
KB Resource Hierarchy
🔖 Ontology (top-level KB resource — direct ACL target)
├── 🧬 EntityOntology ← [:CONTAINS] — inherits from Ontology
├── 🔗 RelationshipOntology ← [:CONTAINS] — inherits from Ontology
└── 🌐 Community ← [:DEFINES] — inherits from Ontology
Community nodes also carry the Dataset label (:Community:Dataset). Every Community is
therefore usable through Dataset-oriented APIs, but the converse does not apply: a Dataset
without the Community label is not an ontology-defined knowledge graph namespace.
Only the Ontology node holds direct GRANT relationships. Children inherit via ancestor-chain
traversal extended to cover both [:CONTAINS] and [:DEFINES] relationships.
Applicable Permissions
| Permission | Ontology | Children (inherited) | Meaning for KB |
|---|---|---|---|
| READ | X | ✓ | View schema and query the KB |
| ADD | X | Add entity/relationship type definitions | |
| UPDATE | X | ✓ | Edit schema definitions |
| DELETE | X | Delete the Ontology | |
| SHARING | X | Manage ontology permissions | |
| OWNERSHIP | X | Transfer ontology ownership |
Roles
Same role table as Documents/Dossiers (Owner, Admin, Manager, Viewer). No new roles are introduced.
Ownership
Each Ontology is owned by a single group via (Group)-[:OWNS]->(Ontology).
When a user creates an Ontology, their hidden personal group is granted Owner permissions
atomically via AclService.SystemGrantPermissions().
The tenant admin group retains full bypass access via the existing SUPER_ADMIN mechanism.
Node Schema Additions
| Node | New Property | Default | Notes |
|---|---|---|---|
Ontology |
isProtected |
false |
Reserved for future use. Since Ontologies have no parent, this currently has no effect on inheritance. |
isDeleted / soft-delete are not added in this phase — Ontology deletion remains hard-delete.
Relationship Schema Additions
| Relationship | From | To | Description |
|---|---|---|---|
[:GRANT:<PERMISSION>] |
Group | Ontology | Direct permission grant — same pattern as Document/Dossier |
[:OWNS] |
Group | Ontology | Owner relationship — mandatory for all non-system Ontologies |
ACL Evaluation
Evaluation order follows the standard Document/Dossier chain:
- Tenant-admin bypass (
IsTenantAdmincheck) — full access - Direct
[:GRANT:<PERMISSION>]on the Ontology node - Inherited grants via ancestor chain (applies to EntityOntology, RelationshipOntology, Community)
The GetAncestorChain query in Neo4jAclRepository is extended from <-[:CONTAINS*]- to
<-[:CONTAINS|DEFINES*]- so Community nodes correctly inherit from their parent Ontology.
The same extension applies to AclQueryBuilder.AclFragment() for query-level filtering.
ACL Enforcement Points
| Operation | Required Permission |
|---|---|
| List / read ontologies | READ |
| Add entity/relationship type | ADD |
| Modify entity/relationship type | UPDATE |
| Delete ontology | DELETE |
| Manage permissions | SHARING |
| Transfer ownership | OWNERSHIP |
Integration with Existing ACL System
KB resources reuse all existing ACL infrastructure without modification:
- IAclService.CheckAccess() / GetAcl() / SetAcl() — label-agnostic, work on any node {id}
- INeo4jAclRepository.MergePermissions() / DeletePermissions() — no changes needed
- ResourceAclModal — wired to Ontology nodes in Ontology.razor via ResourceId = ontology.Id
The two infrastructure changes required are:
1. Extend the GetAncestorChain Cypher query in Neo4jAclRepository to traverse [:DEFINES].
2. Extend AclQueryBuilder.AclFragment() to traverse [:DEFINES] for ACL-filtered list queries.
Bootstrap / Migration
On startup, BootstrapService calls INeo4jOntologies.BootstrapOntologyAcl(), which grants
Owner permissions to the tenant admin group on all Ontology nodes that have no GRANT relationships.
This is idempotent and runs on every startup (no-op after first run).