Open, Attach, and Rebuild
Open, Attach, and Rebuild
These three concepts do different jobs:
open(): prepare local runtime state and restore or create the current internal source identityattach(userId): attach or resume an authenticated account scoperebuild(): explicitly recover local managed state from the authoritative remote snapshot
open()
open() is local-only. It:
- validates sync-managed table metadata
- installs local triggers and guards
- repairs interrupted lifecycle metadata
- restores or creates the current internal source identity
- captures pre-existing managed rows once when bootstrap policy allows it
It never talks to the server.
client.open().getOrThrow()
The current source identity is oversqlite-managed. App code does not pass it to open().
attach(userId)
attach(userId) is the authenticated lifecycle step.
when (val result = client.attach(userId).getOrThrow()) {
is AttachResult.Connected -> {
when (result.outcome) {
AttachOutcome.RESUMED_ATTACHED_STATE -> Unit
AttachOutcome.USED_REMOTE_STATE -> Unit
AttachOutcome.SEEDED_FROM_LOCAL -> Unit
AttachOutcome.STARTED_EMPTY -> Unit
}
}
is AttachResult.RetryLater -> Unit
}
Important points:
- call it whenever an authenticated session exists
open()success is not a substitute forattach(userId)RetryLateris a normal lifecycle response, not an auth error- successful destructive logout later rotates to a fresh internal source before the next attach
rebuild()
rebuild() is the explicit recovery entry point.
val report = client.rebuild().getOrThrow()
Important points:
- it remains an attached/authenticated operation
- it rebuilds local managed state from the authoritative snapshot
- oversqlite chooses the internal mode
- app code does not provide a replacement source id
If the current source is still valid, rebuild keeps it.
If the current source has become stale or out-of-order, rebuild preserves frozen unsynced intent, rebuilds from snapshot, rotates to a fresh internal source, and restores the frozen intent under that fresh source stream.
Successful destructive detach() is the other internal source-rotation point. Unlike rebuild, it
retires the current local sync incarnation because managed local account data was wiped.
Typical App Flow
client.open().getOrThrow()
if (currentUserId != null) {
when (val attach = client.attach(currentUserId).getOrThrow()) {
is AttachResult.Connected -> client.sync().getOrThrow()
is AttachResult.RetryLater -> Unit
}
}
Typical Logout Flow
val result = client.syncThenDetach().getOrThrow()
if (!result.isSuccess()) {
// Ask the UI to stop producing new writes or keep the user attached.
}
If syncThenDetach() succeeds, the next signed-in session attaches through a fresh oversqlite
source stream.