You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: pages/docs/tutorial/tips.md
+9Lines changed: 9 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -256,3 +256,12 @@ This approach allows you to:
256
256
The mapping is stored within the document, so it automatically synchronizes across all peers and persists with the document's state.
257
257
258
258
</details>
259
+
260
+
---
261
+
262
+
##### You can use https://loro.dev/llms-full.txt to prompt your AI
263
+
264
+
When working with AI assistants or language models on Loro-related tasks, you can use these URLs to provide comprehensive context about Loro's capabilities and API:
265
+
266
+
-`https://loro.dev/llms-full.txt` - All the documentation in one file
267
+
-`https://loro.dev/llms.txt` - An overview of Loro website
- You can use `doc.getMap("user." + userId)` instead of `doc.getMap("user").getOrCreateContainer(userId, new LoroMap())` to avoid this problem.
1989
1989
</details>
1990
1990
1991
+
---
1992
+
1993
+
##### Use redaction to safely share document history
1994
+
1995
+
There are times when users might accidentally paste sensitive information (like API keys, passwords, or personal data) into a collaborative document. When this happens, you need a way to remove just that sensitive content from the document history without compromising the rest of the document's integrity.
1996
+
1997
+
<details>
1998
+
<summary>How to safely redact sensitive content</summary>
1999
+
2000
+
Loro provides a `redactJsonUpdates` function that allows you to selectively redact operations within specific version ranges.
2001
+
2002
+
For example, if a user accidentally pastes a password or API key into a document:
2003
+
2004
+
```typescript
2005
+
const doc = new LoroDoc();
2006
+
doc.setPeerId("1");
2007
+
2008
+
// Create some content to be redacted
2009
+
const text = doc.getText("text");
2010
+
text.insert(0, "Sensitive information");
2011
+
doc.commit();
2012
+
2013
+
const map = doc.getMap("map");
2014
+
map.set("password", "secret123");
2015
+
map.set("public", "public information");
2016
+
doc.commit();
2017
+
2018
+
// Export JSON updates
2019
+
const jsonUpdates = doc.exportJsonUpdates();
2020
+
2021
+
// Define version range to redact (redact the text content)
2022
+
const versionRange = {
2023
+
"1": [0, 21] // Redact the "Sensitive information"
This approach is safer than manually editing document content because:
2051
+
2052
+
1. It maintains document structure and CRDT consistency
2053
+
2. It keeps key metadata like operation IDs and dependencies intact
2054
+
3. It allows concurrent editing to continue working after redaction
2055
+
4. It selectively redacts only specific operations, not the entire document
2056
+
2057
+
The redaction process follows these rules:
2058
+
- Preserves delete, tree move, and list move operations
2059
+
- Replaces text insertion content with Unicode replacement characters '�'
2060
+
- Substitutes list and map insert values with null
2061
+
- Maintains structure of child containers
2062
+
- Replaces text mark values with null
2063
+
- Preserves map keys and text annotation keys
2064
+
2065
+
**Important**: Your application needs to ensure that all peers receive the redacted version, otherwise the original document with sensitive information will still exist on other peers.
2066
+
2067
+
</details>
2068
+
2069
+
---
2070
+
2071
+
##### Use shallow snapshots to completely remove old history
2072
+
2073
+
When you need to completely remove ALL history older than a certain version point, shallow snapshots provide the solution.
2074
+
2075
+
<details>
2076
+
<summary>How to remove old history with shallow snapshots</summary>
2077
+
2078
+
Shallow snapshots create a new document that preserves the current state but completely eliminates all history before a specified point, similar to Git's shallow clone functionality.
2079
+
2080
+
```typescript
2081
+
const doc = new LoroDoc();
2082
+
doc.setPeerId("1");
2083
+
2084
+
// Old history - will be completely removed
2085
+
const text = doc.getText("text");
2086
+
text.insert(0, "This document has a long history with many edits");
2087
+
doc.commit();
2088
+
text.insert(0, "Including some potentially sensitive information. ");
2089
+
doc.commit();
2090
+
2091
+
// More recent history - will be preserved
2092
+
text.delete(11, 55); // Remove the middle part
2093
+
text.insert(11, "with sanitized history");
2094
+
doc.commit();
2095
+
2096
+
// Create a sanitized version that removes ALL history before current point
2097
+
const sanitizedSnapshot = doc.export({
2098
+
mode: "shallow-snapshot",
2099
+
frontiers: doc.oplogFrontiers()
2100
+
});
2101
+
2102
+
// Create a new document from the sanitized snapshot
// But ALL history before the snapshot point is completely removed
2111
+
console.log(sanitizedDoc.isShallow()); // true
2112
+
console.log(sanitizedDoc.shallowSinceFrontiers()); // Shows the starting point
2113
+
```
2114
+
2115
+
This approach is useful for:
2116
+
2117
+
1. Completely removing all old history that might contain various sensitive information
2118
+
2. Significantly reducing document size by eliminating unnecessary history
2119
+
3. Creating clean document instances after certain milestones
2120
+
4. Ensuring old operations cannot be recovered or examined
2121
+
2122
+
Compared to redaction:
2123
+
- Shallow snapshots completely remove all operations before a version point
2124
+
- Redaction selectively replaces just specific content with placeholders
2125
+
2126
+
**Important**: While both methods maintain future synchronization consistency, your application must distribute the sanitized document to all peers. Otherwise, the original document with sensitive information will still exist on other clients.
2127
+
2128
+
**When to use each approach**:
2129
+
- Use **redaction** when you need to sanitize specific operations (like an accidental password paste) while preserving older history
2130
+
- Use **shallow snapshots** when you want to completely eliminate all history before a certain point
2131
+
2132
+
</details>
2133
+
2134
+
---
2135
+
2136
+
##### You can store mappings between LoroDoc's peerIds and user IDs in the document itself
2137
+
2138
+
Use `doc.subscribeFirstCommitFromPeer(listener)` to associate peer information with user identities when a peer first interacts with the document.
2139
+
2140
+
<details>
2141
+
<summary>How to track peer-to-user mappings</summary>
2142
+
2143
+
This functionality is essential for building user-centric features in collaborative applications. You often need bidirectional mapping between user IDs and peer IDs:
2144
+
2145
+
- **Finding all edits by a user**: When you need to retrieve all document edits made by a specific user ID, you must first find all peer IDs associated with that user
2146
+
- **Showing edit attribution**: When displaying which user edited a piece of text, you need to map from the peer ID (stored in the operation) back to the user ID for display
2147
+
2148
+
This hook provides an ideal point to associate peer information (such as author identity) with the document. The listener is triggered on the first commit from each peer, allowing you to store user metadata within the document itself.
1. Automatically track which peers have contributed to the document
2164
+
2. Store user metadata (names, emails, etc.) alongside the document
2165
+
3. Build features like author attribution, presence indicators, or edit history
2166
+
2167
+
The mapping is stored within the document, so it automatically synchronizes across all peers and persists with the document's state.
2168
+
2169
+
</details>
2170
+
2171
+
---
2172
+
2173
+
##### You can use https://loro.dev/llms-full.txt to prompt your AI
2174
+
2175
+
When working with AI assistants or language models on Loro-related tasks, you can use these URLs to provide comprehensive context about Loro's capabilities and API:
2176
+
2177
+
- `https://loro.dev/llms-full.txt` - All the documentation in one file
2178
+
- `https://loro.dev/llms.txt` - An overview of Loro website
2179
+
1991
2180
1992
2181
# FILE: pages/docs/tutorial/map.md
1993
2182
@@ -2602,6 +2791,107 @@ of operations will converge to the same document state, obviating concerns about
2602
2791
the order, duplication, or timing of operation delivery.
description: "How to use Loro's ephemeral store feature to implement user awareness and online status management in real-time collaboration."
2799
+
---
2800
+
2801
+
# Ephemeral Store
2802
+
2803
+
In real-time collaborative scenarios, Presence information is just as important as maintaining document consistency across peers through CRDTs. This includes information such as the current collaborator's username, mouse pointer position, or selected objects. We need a mechanism that doesn't persist in the CRDT Document but remains ephemeral, allowing collaborators to perceive each other's presence for better coordination and to avoid conflicts when multiple users edit the same object. This is why we've introduced the Ephemeral Store.
2804
+
2805
+

2806
+
2807
+
Since Ephemeral information is primarily used for real-time collaboration, we've chosen a simple yet effective approach. The Ephemeral Store is a timestamp-based, last-write-wins key-value store. Each entry maintains its own timestamp of the last update, enabling the system to send only the updated entry content rather than the complete current state.
// Listen to changes from `local`, `remote`, or `timeout` events
2834
+
});
2835
+
```
2836
+
2837
+
## API
2838
+
2839
+
- `constructor(timeout)`:
2840
+
Creates a new EphemeralStore instance with an optional timeout parameter (default: 30000ms). The timeout determines how long ephemeral data remains valid before being automatically removed.
2841
+
2842
+
- `set(key, value)`:
2843
+
Sets a value for the specified key in the ephemeral store. If the key already exists, its value will be updated.
2844
+
2845
+
- `get(key)`:
2846
+
Retrieves the current value for the specified key, or returns `undefined` if the key doesn't exist.
2847
+
2848
+
- `delete(key)`:
2849
+
Removes the specified key and its associated value from the ephemeral store.
2850
+
2851
+
- `getAllStates()`:
2852
+
Returns all current key-value pairs in the ephemeral store.
2853
+
2854
+
- `keys()`:
2855
+
Returns an array of all keys currently in the ephemeral store.
2856
+
2857
+
- `encode(key)`:
2858
+
Encodes the value associated with the specified key into a binary format that can be transmitted to other peers.
2859
+
2860
+
- `encodeAll()`:
2861
+
Encodes all key-value pairs in the ephemeral store into a binary format.
2862
+
2863
+
- `apply(bytes)`:
2864
+
Applies encoded ephemeral data received from other peers to the local ephemeral store.
Registers a listener function that will be called whenever the ephemeral store is updated, either from local changes, remote changes, or timeout events.
2868
+
```ts
2869
+
interface EphemeralStoreEvent {
2870
+
// The source of the event: local changes, imported from remote, or timeout expiration
Note: A shallow document only contains history after a certain version point. Operations before the shallow start point are not included, but the document remains fully functional for collaboration.
3894
4184
4185
+
### Redacting Sensitive Content
4186
+
4187
+
Loro allows you to redact specific segments of document history while preserving the rest. This is particularly useful when:
4188
+
4189
+
1. A user accidentally pastes sensitive information (like passwords or API keys) into the document
4190
+
2. You need to remove just the sensitive part of the history while keeping older and newer edits intact
4191
+
3. You want to share document history with sensitive segments sanitized
4192
+
4193
+
Here's how to use the redaction functionality:
4194
+
4195
+
```typescript
4196
+
const doc = new LoroDoc();
4197
+
doc.setPeerId("1");
4198
+
4199
+
// Create some content to be redacted
4200
+
const text = doc.getText("text");
4201
+
text.insert(0, "Sensitive information");
4202
+
doc.commit();
4203
+
4204
+
const map = doc.getMap("map");
4205
+
map.set("password", "secret123");
4206
+
map.set("public", "public information");
4207
+
doc.commit();
4208
+
4209
+
// Export JSON updates
4210
+
const jsonUpdates = doc.exportJsonUpdates();
4211
+
4212
+
// Define version range to redact (redact the text content)
4213
+
const versionRange = {
4214
+
"1": [0, 21] // Redact the "Sensitive information"
Redaction applies these rules to preserve document structure while removing sensitive content:
4234
+
- Preserves delete and move operations
4235
+
- Replaces text insertion content with Unicode replacement characters '�'
4236
+
- Substitutes list and map insert values with null
4237
+
- Maintains structure of nested containers
4238
+
- Replaces text mark values with null
4239
+
- Preserves map keys and text annotation keys
4240
+
4241
+
Note that redaction doesn't remove the operations completely - it just replaces the sensitive content with placeholders. If you need to completely remove portions of history, see the section on shallow snapshots in the [Tips](./tips.md) section.
4242
+
4243
+
#### Important: Synchronization Considerations
4244
+
4245
+
Both redaction and shallow snapshots maintain future synchronization consistency, but your application is responsible for ensuring all peers get the sanitized version. Otherwise, old instances of the document with sensitive information will still exist on other peers.
0 commit comments