Repository navigation
ACL should be set to Read Append only #16
Copy link
Copy link
Open
Description
Activity
- changed the title
[-]ACL should be set to `Append` only[/-][+]ACL should be set to `Read Append` only[/+]on Jan 10, 2026 Deep Research Analysis: ACL Read Append
Current State ✅
The current solid-chat/app ACL setup is correct for public chats:
# Public: Read + Append (can post, can't edit/delete others) <#public> a acl:Authorization ; acl:agentClass foaf:Agent ; acl:mode acl:Read, acl:Append . # Owner: Full control <#owner> a acl:Authorization ; acl:agent <owner-webid> ; acl:mode acl:Read, acl:Write, acl:Control .
This aligns with the Solid Chat Specification.
The Problem
⚠️ Edit/Delete currently uses destructive SPARQL operations:
// Edit - requires Write permission const updateQuery = ` DELETE { <${msgUri}> sioc:content ?old } INSERT { <${msgUri}> sioc:content "new text" } WHERE { <${msgUri}> sioc:content ?old } ` // Delete - requires Write permission const deleteQuery = `DELETE DATA { ${triples} }`
These operations won't work with Append-only ACLs because:
DELETErequires Write permission- Participants only have Append permission
Solid Chat Spec Solution: Append-Only Patterns
Use
dct:replacesandschema:dateDeletedinstead of destructive deletes:# Original message <#msg-123> a sioc:Post ; sioc:content "Hello" ; dct:created "2026-01-11T10:00:00Z" ; foaf:maker <https://alice.example/#me> . # "Delete" by appending (works with Append permission) <#msg-123-deleted> a sioc:Post ; dct:replaces <#msg-123> ; schema:dateDeleted "2026-01-11T12:00:00Z" ; foaf:maker <https://alice.example/#me> . # "Edit" by appending (works with Append permission) <#msg-123-edited> a sioc:Post ; dct:replaces <#msg-123> ; sioc:content "Hello (edited)" ; dct:created "2026-01-11T12:00:00Z" ; foaf:maker <https://alice.example/#me> .
Benefits:
- ✅ Works with Append-only ACLs
- ✅ Maintains full audit trail
- ✅ No destructive operations
- ✅ Follows Solid Chat spec
Security Comparison
Permission Post Edit Own Delete Own Edit Others Delete Others Read+Append ✅ ✅ (append) ✅ (append) ❌ ❌ Read+Write ✅ ✅ ✅ ⚠️ YES⚠️ YESSecurity Risk: With Write permission, any authenticated user can delete/modify ANY message in the chat!
Implementation Recommendations
1. Refactor Delete to Append-Only
// CURRENT (requires Write) async function deleteMessage(msgUri) { const deleteQuery = `DELETE DATA { ... }` await store.fetcher.webOperation('PATCH', doc, { body: deleteQuery }) } // RECOMMENDED (works with Append) async function deleteMessage(msgUri, doc) { const deletionUri = `${doc.value}#deletion-${Date.now()}` const ins = [ $rdf.st($rdf.sym(deletionUri), DCT('replaces'), $rdf.sym(msgUri), doc), $rdf.st($rdf.sym(deletionUri), SCHEMA('dateDeleted'), $rdf.lit(new Date().toISOString()), doc), $rdf.st($rdf.sym(deletionUri), FOAF('maker'), $rdf.sym(currentUser), doc) ] await store.updater.update([], ins) }
2. Refactor Edit to Append-Only
// RECOMMENDED (works with Append) async function editMessage(msgUri, newContent, doc) { const editUri = `${doc.value}#edit-${Date.now()}` const ins = [ $rdf.st($rdf.sym(editUri), DCT('replaces'), $rdf.sym(msgUri), doc), $rdf.st($rdf.sym(editUri), SIOC('content'), $rdf.lit(newContent), doc), $rdf.st($rdf.sym(editUri), FOAF('maker'), $rdf.sym(currentUser), doc) ] await store.updater.update([], ins) }
3. Update Message Rendering
// When loading messages, check for replacements function isMessageDeleted(msgUri, store, doc) { const replacement = store.any(null, DCT('replaces'), msgUri, doc) if (replacement) { const deleted = store.any(replacement, SCHEMA('dateDeleted'), null, doc) return !!deleted } return false } function getLatestContent(msgUri, store, doc) { const replacement = store.any(null, DCT('replaces'), msgUri, doc) if (replacement) { const newContent = store.any(replacement, SIOC('content'), null, doc) if (newContent) return { content: newContent.value, edited: true } } return { content: store.any(msgUri, SIOC('content'))?.value, edited: false } }
4. UI Indicators
// Show edit/delete status if (isMessageDeleted(msgUri)) { messageEl.classList.add('deleted') messageEl.textContent = '(message deleted)' } else { const { content, edited } = getLatestContent(msgUri) messageEl.textContent = content if (edited) { messageEl.appendChild(createEditedBadge()) // Shows "(edited)" } }
Summary
Aspect Current Recommended ACL Setup ✅ Correct (Read+Append) Keep as-is Edit Method ❌ SPARQL DELETE+INSERT Use dct:replacesappendDelete Method ❌ SPARQL DELETE Use schema:dateDeletedappendAudit Trail ❌ None (data destroyed) ✅ Full history preserved Security ⚠️ Relies on ACL✅ Append-only by design
References
Metadata
Metadata
Assignees
Labels
No labels
Actually the chat ACL is not set and uses default from parent chat folder
Read WriteforauthenticatedAgentit will mean anyone logged can edit anyyear/month/day/chatttlRead AppendforauthenticatedAgentas the draw-back that edit cannot be done directly you need to add a replacement contentThis is expected by the Chat specification https://solid.github.io/chat/#access-control
The consequence is https://solid.github.io/chat/#modified for deleted and modified messages.