@@ -22,15 +22,23 @@ import type { EncryptedDbChange, Storage } from "./Storage.js";
2222import { TimestampBytes } from "./Timestamp.js" ;
2323
2424/**
25- * The Owner represents ownership of data in Evolu. Every database change is
26- * assigned to an owner, enabling sync functionality and access control.
25+ * {@link Owner } without a {@link OwnerWriteKey }.
2726 *
28- * Owners enable **partial sync** - applications can choose which owners to
29- * sync, allowing selective data synchronization based on specific needs.
27+ * @see {@link createSharedReadonlyOwner }
28+ */
29+ export interface ReadonlyOwner {
30+ readonly id : OwnerId ;
31+ readonly encryptionKey : OwnerEncryptionKey ;
32+ }
33+
34+ /**
35+ * The Owner represents ownership of data in Evolu. Every database change is
36+ * assigned to an owner and encrypted with its {@link OwnerEncryptionKey}. Owners
37+ * allow partial sync, only the {@link AppOwner} is synced by default.
3038 *
31- * Owners also provide ** real data deletion** - while individual changes in
32- * local-first/distributed systems can only be marked as deleted, entire owners
33- * can be completely deleted from both relays and devices (except for
39+ * Owners can also provide real data deletion, while individual changes in
40+ * local-first/distributed systems can only be soft deleted, entire owners can
41+ * be completely deleted from both relays and devices (except for
3442 * {@link AppOwner}, which must be preserved for sync coordination).
3543 *
3644 * Evolu provides different owner types depending on their use case:
@@ -46,36 +54,37 @@ import { TimestampBytes } from "./Timestamp.js";
4654 * SLIP-21, ensuring secure and deterministic key generation:
4755 *
4856 * - {@link OwnerId}: Globally unique public identifier
49- * - {@link EncryptionKey }: Symmetric encryption key for data protection
57+ * - {@link OwnerEncryptionKey }: Symmetric encryption key for data protection
5058 * - {@link OwnerWriteKey}: Authentication token for write operations (rotatable)
5159 *
52- * @see {@link createOwner }
60+ * @see {@link createAppOwner }
61+ * @see {@link createShardOwner }
62+ * @see {@link createSharedOwner }
63+ * @see {@link createSharedReadonlyOwner }
5364 */
54- export interface Owner {
55- readonly id : OwnerId ;
56- readonly encryptionKey : OwnerEncryptionKey ;
65+ export interface Owner extends ReadonlyOwner {
5766 readonly writeKey : OwnerWriteKey ;
5867}
5968
60- /**
61- * OwnerId is a branded {@link Id} that uniquely identifies an {@link Owner}.
62- * Branded from {@link Id} to leverage existing helpers like {@link idToIdBytes}.
63- */
69+ /** OwnerId is a branded {@link Id} that uniquely identifies an {@link Owner}. */
6470export const OwnerId = brand ( "OwnerId" , Id ) ;
6571export type OwnerId = typeof OwnerId . Type ;
6672
6773/** Bytes representation of {@link OwnerId}. */
6874export const OwnerIdBytes = brand ( "OwnerIdBytes" , IdBytes ) ;
6975export type OwnerIdBytes = typeof OwnerIdBytes . Type ;
7076
77+ /** Converts {@link OwnerId} to {@link OwnerIdBytes}. */
7178export const ownerIdToOwnerIdBytes = ( ownerId : OwnerId ) : OwnerIdBytes =>
7279 idToIdBytes ( ownerId ) as OwnerIdBytes ;
7380
81+ /** Converts {@link OwnerIdBytes} to {@link OwnerId}. */
7482export const ownerIdBytesToOwnerId = ( ownerIdBytes : OwnerIdBytes ) : OwnerId =>
7583 idBytesToId ( ownerIdBytes as IdBytes ) as OwnerId ;
7684
7785export const ownerWriteKeyLength = NonNegativeInt . orThrow ( 16 ) ;
7886
87+ /** Symmetric encryption key for {@link Owner} data protection. */
7988export const OwnerEncryptionKey = brand ( "OwnerEncryptionKey" , EncryptionKey ) ;
8089export type OwnerEncryptionKey = typeof OwnerEncryptionKey . Type ;
8190
@@ -86,6 +95,16 @@ export type OwnerEncryptionKey = typeof OwnerEncryptionKey.Type;
8695export const OwnerWriteKey = brand ( "OwnerWriteKey" , Entropy16 ) ;
8796export type OwnerWriteKey = typeof OwnerWriteKey . Type ;
8897
98+ /**
99+ * Creates a new random {@link OwnerWriteKey} for rotation.
100+ *
101+ * The initial OwnerWriteKey is deterministically derived from
102+ * {@link OwnerSecret}. Use `createOwnerWriteKey` to rotate (replace) the write
103+ * key without changing the owner identity.
104+ */
105+ export const createOwnerWriteKey = ( deps : RandomBytesDep ) : OwnerWriteKey =>
106+ deps . randomBytes . create ( 16 ) as OwnerWriteKey ;
107+
89108/**
90109 * 32 bytes of cryptographic entropy used to derive {@link Owner} keys.
91110 *
@@ -107,22 +126,11 @@ export const ownerSecretToMnemonic = (secret: OwnerSecret): Mnemonic =>
107126export const mnemonicToOwnerSecret = ( mnemonic : Mnemonic ) : OwnerSecret =>
108127 bip39 . mnemonicToEntropy ( mnemonic , wordlist ) as OwnerSecret ;
109128
110- /** Creates a randomly generated {@link OwnerWriteKey}. */
111- export const createOwnerWriteKey = ( deps : RandomBytesDep ) : OwnerWriteKey =>
112- deps . randomBytes . create ( 16 ) as OwnerWriteKey ;
113-
114129/**
115130 * Creates an {@link Owner} from a {@link OwnerSecret} using SLIP-21 key
116131 * derivation.
117- *
118- * This is an internal helper function, use:
119- *
120- * - {@link createAppOwner}
121- * - {@link createShardOwner}
122- * - {@link createSharedOwner}
123- * - {@link createSharedReadonlyOwner}
124132 */
125- export const createOwner = ( secret : OwnerSecret ) : Owner => ( {
133+ const createOwner = ( secret : OwnerSecret ) : Owner => ( {
126134 id : ownerIdBytesToOwnerId (
127135 OwnerIdBytes . orThrow (
128136 createSlip21 ( secret , [ "Evolu" , "OwnerIdBytes" ] ) . slice ( 0 , 16 ) ,
@@ -182,9 +190,9 @@ export interface AppOwner extends Owner {
182190
183191/** Creates an {@link AppOwner} from an {@link OwnerSecret}. */
184192export const createAppOwner = ( secret : OwnerSecret ) : AppOwner => ( {
193+ ...createOwner ( secret ) ,
185194 type : "AppOwner" ,
186195 mnemonic : ownerSecretToMnemonic ( secret ) ,
187- ...createOwner ( secret ) ,
188196} ) ;
189197
190198/**
@@ -200,18 +208,13 @@ export const createAppOwner = (secret: OwnerSecret): AppOwner => ({
200208 */
201209export interface ShardOwner extends Owner {
202210 readonly type : "ShardOwner" ;
203- readonly transports ?: ReadonlyArray < OwnerTransport > ;
204211}
205212
206213/** Creates a {@link ShardOwner} from an {@link OwnerSecret}. */
207- export const createShardOwner = (
208- secret : OwnerSecret ,
209- transports ?: ReadonlyArray < OwnerTransport > ,
210- ) : ShardOwner => {
214+ export const createShardOwner = ( secret : OwnerSecret ) : ShardOwner => {
211215 return {
212- type : "ShardOwner" ,
213216 ...createOwner ( secret ) ,
214- ... ( transports && { transports } ) ,
217+ type : "ShardOwner" ,
215218 } ;
216219} ;
217220
@@ -236,21 +239,18 @@ export const createShardOwner = (
236239export const deriveShardOwner = (
237240 owner : AppOwner ,
238241 path : NonEmptyReadonlyArray < string | number > ,
239- transports ?: ReadonlyArray < OwnerTransport > ,
240242) : ShardOwner => {
241243 const secret = createSlip21 ( owner . encryptionKey , path ) as OwnerSecret ;
242244
243245 return {
244- type : "ShardOwner" ,
245246 ...createOwner ( secret ) ,
246- ... ( transports && { transports } ) ,
247+ type : "ShardOwner" ,
247248 } ;
248249} ;
249250
250251/** An {@link Owner} for collaborative data with write access. */
251252export interface SharedOwner extends Owner {
252253 readonly type : "SharedOwner" ;
253- readonly transports ?: ReadonlyArray < OwnerTransport > ;
254254}
255255
256256/**
@@ -260,27 +260,18 @@ export interface SharedOwner extends Owner {
260260 * Use {@link createSharedReadonlyOwner} to create a read-only version for
261261 * sharing.
262262 */
263- export const createSharedOwner = (
264- secret : OwnerSecret ,
265- transports ?: ReadonlyArray < OwnerTransport > ,
266- ) : SharedOwner => {
267- return {
268- type : "SharedOwner" ,
269- ...createOwner ( secret ) ,
270- ...( transports && { transports } ) ,
271- } ;
272- } ;
263+ export const createSharedOwner = ( secret : OwnerSecret ) : SharedOwner => ( {
264+ ...createOwner ( secret ) ,
265+ type : "SharedOwner" ,
266+ } ) ;
273267
274268/**
275269 * Read-only version of a {@link SharedOwner} for data sharing. Contains only the
276270 * {@link OwnerId} and {@link EncryptionKey} needed for others to read the shared
277271 * data without write access.
278272 */
279- export interface SharedReadonlyOwner {
273+ export interface SharedReadonlyOwner extends ReadonlyOwner {
280274 readonly type : "SharedReadonlyOwner" ;
281- readonly id : OwnerId ;
282- readonly encryptionKey : EncryptionKey ;
283- readonly transports ?: ReadonlyArray < OwnerTransport > ;
284275}
285276
286277/** Creates a {@link SharedReadonlyOwner} from a {@link SharedOwner}. */
@@ -290,7 +281,6 @@ export const createSharedReadonlyOwner = (
290281 type : "SharedReadonlyOwner" ,
291282 id : sharedOwner . id ,
292283 encryptionKey : sharedOwner . encryptionKey ,
293- ...( sharedOwner . transports && { transports : sharedOwner . transports } ) ,
294284} ) ;
295285
296286/**
@@ -383,8 +373,8 @@ export const parseOwnerIdFromOwnerWebSocketTransportUrl = (
383373 url : string ,
384374) : OwnerId | null => getOrNull ( OwnerId . fromUnknown ( url . split ( "=" ) [ 1 ] ) ) ;
385375
386- /** Base interface for all owner errors. */
387- export interface BaseOwnerError {
376+ /** Common interface implemented by all owner domain errors. */
377+ export interface OwnerError {
388378 readonly ownerId : OwnerId ;
389379}
390380
0 commit comments