Skip to content

Commit af1e668

Browse files
committed
Owners refactor and external AppOwner support
1 parent a39d298 commit af1e668

21 files changed

Lines changed: 2752 additions & 543 deletions

File tree

.changeset/hot-peas-own.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
"@evolu/common": patch
3+
---
4+
5+
# Owners refactor and external AppOwner support
6+
7+
## 🚀 Features
8+
9+
- **External AppOwner Support**: `AppOwner` can now be created from external keys without sharing the mnemonic with the Evolu app. The `mnemonic` property is now optional, allowing for better security when integrating with external authentication systems.
10+
11+
- **New Config Option**: Added `initialAppOwner` configuration option to specify a pre-existing AppOwner when creating an Evolu instance, replacing the previous `mnemonic` option for better encapsulation.
12+
13+
## 🔄 Breaking Changes
14+
15+
- **Owner API Redesign**: Complete refactor of the Owner system with cleaner, more focused interfaces:
16+
- Simplified `Owner` interface with only essential properties (`id`, `encryptionKey`, `writeKey`)
17+
- Removed temporal properties (`createdAt`, `timestamp`) from core Owner interface
18+
- Eliminated complex `OwnerRow` and `OwnerWithWriteAccess` types
19+
20+
- **Database Schema Changes**:
21+
- Replaced `evolu_owner` table with streamlined `evolu_config` table
22+
- New `evolu_version` table for protocol versioning
23+
- Simplified storage of AppOwner data in single config row
24+
25+
- **Configuration Changes**:
26+
- `Config.mnemonic` replaced with `Config.initialAppOwner`
27+
- More explicit control over owner initialization
28+
29+
## ✨ Improvements
30+
31+
- **Enhanced Documentation**: Comprehensive JSDoc with clear explanations of owner types, use cases, and examples
32+
- **Clock Management**: New internal clock system for better timestamp handling
33+
- **Test Coverage**: Extensive test suite covering all owner types and edge cases
34+
35+
## 🔧 Internal Changes
36+
37+
- **Database Initialization**: Refactored database setup to use new schema with better separation of concerns
38+
- **Protocol Updates**: Updated to protocol version 0 with new storage format

apps/web/src/components/NextJsExample.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const formatTypeError = createFormatTypeError<
103103

104104
const evolu = createEvolu(evoluReactWebDeps)(Schema, {
105105
reloadUrl: "/docs/examples/react/nextjs",
106-
name: getOrThrow(SimpleName.from("evolu-nextjs-example-v010725")),
106+
name: getOrThrow(SimpleName.from("evolu-nextjs-example-v090725")),
107107

108108
...(process.env.NODE_ENV === "development" && {
109109
syncUrl: "http://localhost:4000",
@@ -560,7 +560,7 @@ const OwnerActions: FC = () => {
560560
onClick={handleDownloadDatabaseClick}
561561
/>
562562
</div>
563-
{showMnemonic && owner != null && (
563+
{showMnemonic && owner?.mnemonic && (
564564
<div>
565565
<textarea
566566
value={owner.mnemonic}

examples/react-electron/src/EvoluDemo.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ const OwnerActions: FC = () => {
433433
onClick={handleDownloadDatabaseClick}
434434
/>
435435
</div>
436-
{showMnemonic && owner != null && (
436+
{showMnemonic && owner?.mnemonic && (
437437
<div>
438438
<textarea
439439
style={{

examples/react-nextjs/components/NextJsExample.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ const OwnerActions: FC = () => {
552552
onClick={handleDownloadDatabaseClick}
553553
/>
554554
</div>
555-
{showMnemonic && owner != null && (
555+
{showMnemonic && owner?.mnemonic && (
556556
<div>
557557
<textarea
558558
value={owner.mnemonic}

examples/react-vite-pwa/src/components/EvoluDemo.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ const OwnerActions: FC = () => {
508508
onClick={handleDownloadDatabaseClick}
509509
/>
510510
</div>
511-
{showMnemonic && owner != null && (
511+
{showMnemonic && owner?.mnemonic && (
512512
<div>
513513
<textarea
514514
value={owner.mnemonic}

packages/common/src/Crypto.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
Uint8Array,
2222
} from "./Type.js";
2323
import { Brand } from "./Types.js";
24+
import { assert } from "./Assert.js";
2425

2526
/** `Uint8Array` created by {@link createRandomBytes}. */
2627
export type RandomBytes = Uint8Array & Brand<"RandomBytes">;
@@ -58,6 +59,11 @@ export const createSlip21 = (
5859
seed: MnemonicSeed,
5960
path: ReadonlyArray<string>,
6061
): Uint8Array => {
62+
assert(
63+
seed.length >= 16 && seed.length <= 64,
64+
`Unusual SLIP-0021 seed length: ${seed.length} bytes`,
65+
);
66+
6167
let m = hmac(sha512, "Symmetric key seed", seed);
6268
for (const component of path) {
6369
const p = new TextEncoder().encode(component);
@@ -100,9 +106,6 @@ export const createSlip21Id = (
100106
export const EncryptionKey = brand("EncryptionKey", length(32)(Uint8Array));
101107
export type EncryptionKey = typeof EncryptionKey.Type;
102108

103-
export const createEncryptionKey = (seed: MnemonicSeed): EncryptionKey =>
104-
createSlip21(seed, ["Evolu", "Encryption Key"]) as EncryptionKey;
105-
106109
/** Symmetric cryptography. */
107110
export interface SymmetricCrypto {
108111
readonly nonceLength: NonNegativeInt;
@@ -185,7 +188,6 @@ export const padmePaddedLength = (length: NonNegativeInt): NonNegativeInt => {
185188
* Returns the PADMÉ padding length for a given input length. Uses
186189
* {@link padmePaddedLength}.
187190
*/
188-
189191
export const padmePaddingLength = (length: NonNegativeInt): NonNegativeInt => {
190192
return (padmePaddedLength(length) - length) as NonNegativeInt;
191193
};

packages/common/src/Evolu/Config.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { ConsoleConfig } from "../Console.js";
22
import { getOrThrow } from "../Result.js";
3-
import { Mnemonic, SimpleName } from "../Type.js";
3+
import { SimpleName } from "../Type.js";
44
import type { DbIndexesBuilder } from "./Kysely.js";
5+
import type { AppOwner } from "./Owner.js";
56

67
export interface Config extends ConsoleConfig {
78
/**
@@ -66,11 +67,10 @@ export interface Config extends ConsoleConfig {
6667
readonly indexes?: DbIndexesBuilder;
6768

6869
/**
69-
* Use this option to create Evolu with the specified mnemonic. If omitted,
70-
* the mnemonic will be autogenerated. That should be the default behavior
71-
* until special UX requirements are needed (e.g., multitenancy).
70+
* Initial AppOwner to use when creating Evolu instance. If omitted, a new
71+
* AppOwner will be generated automatically.
7272
*/
73-
readonly mnemonic?: Mnemonic;
73+
readonly initialAppOwner?: AppOwner;
7474

7575
/**
7676
* Use in-memory SQLite database instead of persistent storage. Useful for

0 commit comments

Comments
 (0)