mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-08-12 06:35:45 +00:00
docs: plan adaptive journal synchronisation
This commit is contained in:
@@ -0,0 +1,520 @@
|
||||
# [PLANNING] Adaptive Journal Sync
|
||||
|
||||
> [!NOTE]
|
||||
> This document is an exploratory developer design. It is not a design freeze, release commitment, migration
|
||||
> instruction, or statement of current user-visible behaviour. The existing opaque Journal pack protocol remains the
|
||||
> implementation baseline while this proposal is evaluated.
|
||||
|
||||
## Summary
|
||||
|
||||
Adaptive Journal Sync would separate lightweight Metadata events from Chunk delivery while preserving one common
|
||||
Journal replication contract across different remote capabilities.
|
||||
|
||||
The logical model would always treat Chunks as content-addressable, immutable values. The physical representation would
|
||||
adapt to the remote:
|
||||
|
||||
- a batch-capable database such as PostgreSQL exposed through PostgREST could store one encrypted Chunk per immutable
|
||||
row and answer multi-key requests through one RPC; and
|
||||
- an object-shaped remote such as S3-compatible Object Storage or WebDAV could aggregate Chunks into immutable packs,
|
||||
then resolve Chunk identifiers through an append-only, versioned catalogue.
|
||||
|
||||
This proposal deliberately separates common semantics from common physical layout. A remote does not need to store
|
||||
Chunks in the same shape as another remote, but every implementation must provide the same batched availability,
|
||||
storage, retrieval, publication, and failure guarantees.
|
||||
|
||||
## Context
|
||||
|
||||
Self-hosted LiveSync already separates each file into one Metadata document and multiple Chunk documents. Metadata
|
||||
contains file properties and Chunk references; it does not contain raw file content.
|
||||
|
||||
The current Journal Replicator serialises Metadata and Chunk document changes into the same compressed and optionally
|
||||
encrypted Journal pack. `IJournalStorage` then transports each pack as an opaque key and byte sequence. This design is a
|
||||
useful compatibility baseline because S3-compatible Object Storage, WebDAV, and PostgREST exercise the same Journal
|
||||
logic.
|
||||
|
||||
The common opaque representation also prevents a storage adapter from making Chunk-specific decisions. By the time an
|
||||
adapter receives a pack, Metadata and Chunks have already been combined, compressed, and encrypted. PostgREST can
|
||||
provide indexed Journal listing, but it cannot use native batched Chunk lookup or relational transactions at this
|
||||
boundary.
|
||||
|
||||
Adaptive Journal Sync proposes a domain-level repository boundary above physical object storage.
|
||||
|
||||
## Relationship to existing designs
|
||||
|
||||
This proposal builds on, rather than silently replaces, the following documents:
|
||||
|
||||
- [The Journal Replicator 2nd Edition](../design_docs_of_journalsync_2nd.md) defines the current streaming pipeline and
|
||||
`IJournalStorage` boundary.
|
||||
- [The design intent explanation for using metadata and chunks](intention_of_chunks.md) records why Metadata and Chunks
|
||||
are separate and why Chunks are content-addressable.
|
||||
- [Tiered Chunk Storage with Live Compaction](tired_chunk_pack.md) and
|
||||
[Tiered Chunk Storage for Bucket Sync](tired_chunk_pack_bucket.md) establish the hot/cold and LSM-tree motivation.
|
||||
- [Chunk Aggregation by Prefix](chunk_aggregation_by_prefix.md) is withdrawn, but records the risks of mutable shared
|
||||
index documents and write-conflict retry loops.
|
||||
- [Chunk Retrieval and Waiting](chunk_retrieval_and_waiting.md) defines the current delivery and terminal-condition
|
||||
contract which any new direct Chunk source must extend deliberately.
|
||||
|
||||
Adaptive Journal Sync refines the earlier tiered proposals by replacing mutable per-client hot logs and shared mutable
|
||||
index documents with immutable Level 0 segments, append-only catalogue deltas, and periodic catalogue snapshots.
|
||||
|
||||
## Goals
|
||||
|
||||
- Keep Metadata and raw file content separate throughout remote publication.
|
||||
- Preserve logical content-addressable storage for Chunks.
|
||||
- Bound network requests by batches or packs rather than by individual Chunk count.
|
||||
- Use native multi-key RPCs and transactions where a remote provides them.
|
||||
- Use immutable pack files and catalogue records where a remote provides only object operations.
|
||||
- Keep ordinary multi-writer publication append-only and free from a central mutable write bottleneck.
|
||||
- Publish referenced Chunks before publishing Metadata which makes those references visible.
|
||||
- Retain end-to-end encryption and document any metadata or equality leakage introduced by indexed Chunk lookup.
|
||||
- Allow the current opaque Journal protocol and the proposed protocol to coexist behind an explicit version boundary.
|
||||
- Make every derived index and cache reconstructible from authoritative remote records.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Replacing Metadata with a server-readable relational file schema.
|
||||
- Storing raw file content inside Metadata rows or events.
|
||||
- Requiring every remote to expose the same physical layout.
|
||||
- Guaranteeing that the same logical Chunk has exactly one physical copy at every moment.
|
||||
- Introducing an implicit migration or rebuild for existing Journal remotes.
|
||||
- Making Garbage Collection safe without an explicit reachability, retention, and device-progress policy.
|
||||
- Committing this design to a release before correctness, privacy, and performance gates have passed.
|
||||
|
||||
## Terminology
|
||||
|
||||
### Logical Chunk
|
||||
|
||||
An immutable content value identified by a Chunk ID. If bytes change, the result is a new logical Chunk with a new ID.
|
||||
An existing logical Chunk is never updated in place.
|
||||
|
||||
### Native CAS
|
||||
|
||||
A remote representation in which one logical Chunk maps to one insert-only value addressable by an opaque Chunk key.
|
||||
PostgreSQL is the initial candidate because it can answer `hasMany`, `getMany`, and `putMany` through bounded batch RPCs.
|
||||
|
||||
### Chunk pack
|
||||
|
||||
An immutable physical object containing one or more independently verifiable encrypted Chunk records. A pack is a
|
||||
request-amortisation and compaction unit, not the logical identity of its Chunks.
|
||||
|
||||
### Catalogue
|
||||
|
||||
The reconstructible mapping from opaque remote Chunk keys to one or more physical pack locations. Its authoritative
|
||||
object-storage representation consists of immutable snapshots and append-only deltas. A local in-memory or persistent
|
||||
catalogue may be mutable because it is derived state.
|
||||
|
||||
### Metadata batch
|
||||
|
||||
An immutable group of Metadata revision events. It refers to logical Chunk IDs and contains no raw Chunk content.
|
||||
|
||||
### Commit manifest
|
||||
|
||||
The final publication record for a Metadata batch. A reader ignores an incomplete batch until its commit manifest is
|
||||
visible and valid.
|
||||
|
||||
## Required invariants
|
||||
|
||||
1. Metadata contains Chunk references, never raw file content.
|
||||
2. A logical Chunk ID identifies immutable bytes.
|
||||
3. A committed Metadata reference is not published before at least one valid remote source for that Chunk is durable.
|
||||
4. Ordinary writers create immutable records. They do not edit an existing pack, catalogue snapshot, or catalogue
|
||||
delta.
|
||||
5. A retry may create a duplicate physical Chunk or pack, but it must not create two logical meanings for one Chunk ID.
|
||||
6. A receiver validates identity and authenticated encryption before accepting a Chunk.
|
||||
7. A derived catalogue, cache, or PostgreSQL materialised index can be discarded and reconstructed.
|
||||
8. Repacking does not require Metadata revision changes because Metadata refers to logical Chunk IDs rather than pack
|
||||
locations.
|
||||
9. Remote reset, protocol migration, compaction, retirement, and deletion remain explicit protected operations.
|
||||
10. The protocol must define a bounded completion or failure condition for each requested Chunk batch.
|
||||
|
||||
## Proposed repository boundary
|
||||
|
||||
`IJournalStorage` remains useful as the current opaque-object transport. Adaptive Journal Sync would introduce a
|
||||
higher-level repository composition:
|
||||
|
||||
```ts
|
||||
interface JournalEventStore {
|
||||
appendMetadataBatch(batch: EncryptedMetadataBatch): Promise<BatchCommit>;
|
||||
listMetadataBatches(after: BatchCursor, limit: number): Promise<EncryptedMetadataBatch[]>;
|
||||
}
|
||||
|
||||
interface ChunkStore {
|
||||
capabilities(): ChunkStoreCapabilities;
|
||||
hasMany(ids: readonly RemoteChunkKey[]): Promise<ChunkAvailability>;
|
||||
putMany(chunks: readonly EncryptedChunk[]): Promise<ChunkPublication>;
|
||||
getMany(ids: readonly RemoteChunkKey[]): Promise<ChunkResult>;
|
||||
}
|
||||
|
||||
interface AdaptiveJournalRepository {
|
||||
events: JournalEventStore;
|
||||
chunks: ChunkStore;
|
||||
}
|
||||
```
|
||||
|
||||
The interface is batched even when the physical remote lacks a native batch operation. The implementation owns request
|
||||
planning and must not make the caller loop over one network request per Chunk.
|
||||
|
||||
Capability selection should describe semantics rather than provider names. Candidate capabilities include:
|
||||
|
||||
- bounded native multi-key lookup;
|
||||
- bounded native multi-value read and write;
|
||||
- atomic batch publication;
|
||||
- conditional object writes;
|
||||
- byte-range reads;
|
||||
- inexpensive ordered listing; and
|
||||
- server-side immutable CAS insertion.
|
||||
|
||||
The first implementation can use a fixed strategy per adapter. Dynamic thresholds may be added only after measurements
|
||||
show that they improve the same contract.
|
||||
|
||||
## Physical strategies
|
||||
|
||||
### Native batch CAS
|
||||
|
||||
PostgREST can expose a Vault-scoped, row-level-security-protected Chunk table:
|
||||
|
||||
```sql
|
||||
create table chunks (
|
||||
vault_id text not null,
|
||||
chunk_key text collate "C" not null,
|
||||
encrypted_body bytea not null,
|
||||
size_bytes bigint not null,
|
||||
created_at timestamptz not null,
|
||||
primary key (vault_id, chunk_key)
|
||||
);
|
||||
```
|
||||
|
||||
The public API would use bounded RPCs rather than one ordinary REST request per row:
|
||||
|
||||
- `has_chunks(keys[])` returns a compact availability result;
|
||||
- `get_chunks(keys[])` returns a framed or streamed binary batch;
|
||||
- `put_chunks(keys[], bodies[])` inserts missing immutable rows and verifies existing identities; and
|
||||
- `commit_metadata_batch(...)` publishes the Metadata batch only after its required Chunks are present.
|
||||
|
||||
The exact request limits must be byte-based as well as count-based. A request containing hundreds of small Chunks and a
|
||||
request containing hundreds of large Chunks do not have equivalent memory or proxy cost.
|
||||
|
||||
PostgreSQL may maintain mutable derived indexes transactionally. The Chunk rows themselves remain immutable CAS values,
|
||||
and any separately maintained operational index must be rebuildable from authoritative rows and batch commits.
|
||||
|
||||
### Immutable pack CAS
|
||||
|
||||
S3-compatible Object Storage and WebDAV do not normally provide multi-key value retrieval. Storing one remote object per
|
||||
Chunk would turn a missing set of 1,000 Chunks into as many as 1,000 existence requests and 1,000 value requests.
|
||||
Parallel HTTP requests reduce elapsed time but do not remove request cost, connection pressure, or service charging.
|
||||
|
||||
The pack strategy groups newly published Chunks into immutable objects:
|
||||
|
||||
```text
|
||||
packs/<pack-id>.bin
|
||||
indexes/<pack-id>.idx
|
||||
catalogue/deltas/<delta-id>
|
||||
```
|
||||
|
||||
The pack index maps each opaque remote Chunk key to its ciphertext offset, ciphertext length, and integrity information.
|
||||
S3-compatible storage can use byte-range reads when the pack format encrypts and authenticates records independently.
|
||||
WebDAV can fetch the complete pack when reliable range reads are unavailable. Both paths expose the same batched
|
||||
`getMany` result.
|
||||
|
||||
Pack targets must be expressed primarily in bytes. The initial experiment should compare small immutable Level 0
|
||||
micro-packs with larger compacted Level 1 packs. A latency-sensitive synchronisation may publish a pack containing one
|
||||
Chunk; it never rewrites an older pack merely to fill free space.
|
||||
|
||||
### Strategy comparison
|
||||
|
||||
| Property | Native batch CAS | Immutable pack CAS |
|
||||
| --- | --- | --- |
|
||||
| Logical identity | Opaque remote Chunk key | Opaque remote Chunk key |
|
||||
| Ordinary write | Insert missing rows | Upload a new pack and index |
|
||||
| Multi-key lookup | One bounded RPC | Local catalogue lookup |
|
||||
| Multi-key read | One bounded or streamed RPC | Group by pack, then range or whole-pack reads |
|
||||
| Physical duplicates | Preventable transactionally | Permitted temporarily |
|
||||
| Ordinary shared mutation | Transactional derived index only | None |
|
||||
| Compaction | Optional row maintenance | Repack live Chunks into a new generation |
|
||||
|
||||
## Pack lifecycle
|
||||
|
||||
A Chunk inside a pack is never changed. A file edit which replaces logical Chunk `B` with logical Chunk `D` produces a
|
||||
new pack only for newly published content:
|
||||
|
||||
```text
|
||||
Old Metadata: [A, B, C]
|
||||
New Metadata: [A, D, C]
|
||||
|
||||
pack-001: [A, B, C] unchanged
|
||||
pack-002: [D] new Level 0 pack
|
||||
```
|
||||
|
||||
`B` may become unreachable, but `pack-001` remains available until a later protected compaction proves that it can be
|
||||
retired. This trades temporary storage amplification for immutable, low-contention writes.
|
||||
|
||||
Small urgent writes may use either a one-batch micro-pack or a loose immutable object. Supporting both loose and packed
|
||||
locations complicates the catalogue, so the first object-store experiment should use micro-packs consistently and add
|
||||
loose objects only when measurements justify them.
|
||||
|
||||
## Versioned catalogue
|
||||
|
||||
### Authoritative state
|
||||
|
||||
The remote object-store catalogue is logically versioned but not rewritten in full for every ordinary write:
|
||||
|
||||
```text
|
||||
catalogue/snapshots/<generation>
|
||||
catalogue/deltas/<writer-id>/<delta-id>
|
||||
catalogue/commits/<commit-id>
|
||||
```
|
||||
|
||||
A delta can register a new pack:
|
||||
|
||||
```json
|
||||
{
|
||||
"add": {
|
||||
"packId": "pack-002",
|
||||
"index": "indexes/pack-002.idx"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The index contains the Chunk-to-location entries, so a catalogue delta stays small. Concurrent writers publish
|
||||
independent immutable deltas; their additions merge as a set and do not compete to replace one shared catalogue object.
|
||||
The same logical Chunk may temporarily resolve to several valid packs.
|
||||
|
||||
### Local derived catalogue
|
||||
|
||||
A client loads the latest trusted snapshot it knows, applies later deltas, and builds a mutable local mapping:
|
||||
|
||||
```text
|
||||
Remote Chunk key -> [pack ID, offset, length, generation]
|
||||
```
|
||||
|
||||
This local catalogue is a cache. Corruption or loss causes reconstruction, not remote data loss.
|
||||
|
||||
An optional small `catalogue/latest` object may point to a recent snapshot. It is an optimisation hint rather than the
|
||||
sole authority. S3-compatible storage can update it with an ETag precondition. A WebDAV implementation which cannot
|
||||
rely on conditional updates can recover by listing valid snapshots and catalogue commits.
|
||||
|
||||
### Snapshotting
|
||||
|
||||
Catalogue deltas eventually make new-device initialisation expensive. A compactor periodically produces a complete
|
||||
immutable snapshot:
|
||||
|
||||
```text
|
||||
snapshot-100 + deltas 101..500 -> snapshot-500
|
||||
```
|
||||
|
||||
The snapshot commit records its covered delta frontier, integrity hash, format version, and predecessor information.
|
||||
Readers apply deltas outside that frontier. Old snapshots and covered deltas remain available for a grace period before
|
||||
protected deletion.
|
||||
|
||||
## Publication protocol
|
||||
|
||||
### Object-store publication
|
||||
|
||||
1. Build and validate a Chunk micro-pack locally.
|
||||
2. Upload the immutable pack under a unique ID.
|
||||
3. Upload its immutable index.
|
||||
4. Publish an immutable catalogue delta which registers the pack.
|
||||
5. Upload the encrypted Metadata batch.
|
||||
6. Publish the batch commit manifest last.
|
||||
7. Advance the local send checkpoint only after the commit manifest succeeds.
|
||||
|
||||
A failure before step 6 may leave unreachable objects, but it does not publish Metadata with unavailable Chunk
|
||||
references. A retry can reuse verified immutable objects or publish replacements under new IDs. Later maintenance can
|
||||
remove unreachable incomplete publications.
|
||||
|
||||
### Native CAS publication
|
||||
|
||||
PostgreSQL can perform Chunk insertion, required-Chunk verification, Metadata batch insertion, and commit publication
|
||||
inside a transaction. The external semantics remain equivalent to the object-store commit-manifest sequence.
|
||||
|
||||
### Receive
|
||||
|
||||
1. List committed Metadata batches after the local cursor.
|
||||
2. Decrypt and validate Metadata events.
|
||||
3. Collect all referenced Chunks missing from the local database.
|
||||
4. Call `getMany` once per bounded byte and count window.
|
||||
5. Let the Chunk Store group object-store requests by pack or issue a native batch RPC.
|
||||
6. Validate and persist Chunks.
|
||||
7. Apply Metadata revisions through the maintained PouchDB revision contract.
|
||||
8. Advance the receive checkpoint only after the batch reaches its terminal state.
|
||||
|
||||
A commit manifest may include non-authoritative location hints for newly published Chunks. Metadata continues to refer
|
||||
only to logical Chunk IDs so that repacking never changes file revisions.
|
||||
|
||||
## Concurrency and failure model
|
||||
|
||||
Ordinary publication is multi-writer and append-only:
|
||||
|
||||
- pack IDs, delta IDs, and batch IDs must be globally collision-resistant;
|
||||
- concurrent publication of the same logical Chunk may create physical duplicates;
|
||||
- catalogue additions merge without a last-write-wins replacement;
|
||||
- failed uploads remain unreachable until maintenance removes them; and
|
||||
- retries are idempotent at the logical Chunk and Metadata revision boundaries.
|
||||
|
||||
Physical retirement and deletion require stronger coordination than addition. A compactor must use a bounded lease or
|
||||
fencing token appropriate to the remote. A stale compactor may upload a redundant replacement pack, but it must not
|
||||
publish a retirement or delete an object after losing its authority.
|
||||
|
||||
The protocol must test each failure boundary independently: pack upload, index upload, catalogue delta publication,
|
||||
Metadata upload, commit publication, checkpoint persistence, snapshot publication, retirement, and deletion.
|
||||
|
||||
## Encryption and privacy
|
||||
|
||||
The current whole-pack encryption can hide document IDs, Chunk IDs, and pack contents from the storage service. Native
|
||||
CAS and searchable pack indexes introduce different leakage, so they require an explicit threat-model review.
|
||||
|
||||
The remote Chunk key should be an opaque Vault-scoped derivation rather than a raw content hash. One candidate is:
|
||||
|
||||
```text
|
||||
remoteChunkKey = HMAC(vaultChunkIdentityKey, localChunkID)
|
||||
```
|
||||
|
||||
This prevents correlation of the same content across independent Vaults while preserving equality and deduplication
|
||||
inside one Vault. It still reveals within-Vault equality, object count, sizes, and access patterns to the remote.
|
||||
|
||||
For byte-range pack reads, each Chunk record must be encrypted and authenticated independently. Encrypting one complete
|
||||
pack as a single AEAD value would require downloading the complete pack before authenticating or decrypting one range.
|
||||
The pack header and index must be authenticated, and the design must define whether the object-store catalogue itself
|
||||
is encrypted.
|
||||
|
||||
PostgREST needs server-visible opaque keys to execute a multi-key query. It does not need plaintext file paths, local
|
||||
Chunk hashes, or decrypted content.
|
||||
|
||||
Credential rotation must not change remote Chunk identity. Rotation of the Vault Chunk identity key is a data migration
|
||||
which creates a new namespace and cannot be treated as an ordinary bearer-token or password change.
|
||||
|
||||
## Garbage Collection and repacking
|
||||
|
||||
Garbage Collection is a reachability operation, not an age-only deletion policy.
|
||||
|
||||
At minimum, the reachability set must protect the current Metadata winner and every live conflict branch. The treatment
|
||||
of non-live retained revision history must be an explicit shared decision with the maintained Garbage Collection
|
||||
specification. A design which silently makes retained history unreadable is not acceptable.
|
||||
|
||||
Object-store compaction follows a generational copy-and-publish process:
|
||||
|
||||
1. Freeze a reachability and catalogue frontier under a valid fencing token.
|
||||
2. Read live Chunks from selected Level 0 and Level 1 packs.
|
||||
3. Write new immutable Level 1 packs and indexes.
|
||||
4. Publish catalogue additions for the new packs.
|
||||
5. Publish a new snapshot or retirement delta which prefers the new generation.
|
||||
6. Retain old packs for the defined device-progress and time-based grace conditions.
|
||||
7. Delete retired packs only after the protection conditions remain satisfied.
|
||||
|
||||
Metadata does not change during repacking because the catalogue provides indirection from logical Chunk IDs to physical
|
||||
locations.
|
||||
|
||||
Native row CAS can delete unreachable rows without repacking, but it must use the same reachability and retention
|
||||
policy. PostgreSQL transactionality simplifies mutation; it does not prove that a Chunk is safe to delete.
|
||||
|
||||
## Compatibility and migration
|
||||
|
||||
Adaptive Journal Sync requires an explicit protocol and capability version recorded in the remote milestone or its
|
||||
successor. A device which understands only opaque Journal packs must not partially consume Metadata-only batches.
|
||||
|
||||
The existing remote layout and the adaptive layout should use disjoint prefixes or schemas during experimentation. An
|
||||
opt-in test Vault may rebuild into the adaptive representation, but an ordinary upgrade must not migrate or delete an
|
||||
existing remote implicitly.
|
||||
|
||||
The compatibility plan must define:
|
||||
|
||||
- negotiation between old and adaptive clients;
|
||||
- rollback before and after the first adaptive commit;
|
||||
- coexistence or exclusion rules for mixed client versions;
|
||||
- checkpoint identity and epoch changes;
|
||||
- remote reset behaviour;
|
||||
- encryption-key and Chunk-identity-key migration; and
|
||||
- exact conditions which require Fetch, Rebuild, or a new remote profile.
|
||||
|
||||
## Performance model
|
||||
|
||||
Adaptive selection must optimise total work rather than request count alone. Measurements need to include:
|
||||
|
||||
- number of network requests;
|
||||
- transferred and over-fetched bytes;
|
||||
- first-Metadata and first-complete-file latency;
|
||||
- CPU, memory, compression, and encryption cost;
|
||||
- remote storage amplification;
|
||||
- catalogue initialisation and update cost;
|
||||
- PostgREST query, transaction, and response framing cost;
|
||||
- S3-compatible request charging and range behaviour; and
|
||||
- WebDAV listing, range support, and whole-pack fallback.
|
||||
|
||||
The benchmark matrix should vary Vault size, retained history, Chunk-size distribution, editing pattern, network
|
||||
round-trip time, proxy path, concurrent writers, and new-device versus incremental synchronisation.
|
||||
|
||||
Initial thresholds must be conservative and observable. Candidate inputs include pending byte count, pending Chunk
|
||||
count, expected pack reuse, range-read capability, and an upper bound on one RPC or pack response. Provider names alone
|
||||
must not become performance policy.
|
||||
|
||||
## Planned implementation stages
|
||||
|
||||
### Stage 0: Executable model
|
||||
|
||||
- Define the repository, Chunk Store, catalogue, and commit state machines independently of a transport.
|
||||
- Add property and fault-injection tests for publication ordering, retry, duplicate physical locations, and recovery.
|
||||
- Record the threat model and remote-visible metadata.
|
||||
|
||||
### Stage 1: Native batch CAS experiment
|
||||
|
||||
- Add disposable PostgreSQL/PostgREST RPCs for batched Chunk availability, insertion, retrieval, and Metadata commit.
|
||||
- Measure RPC framing, transaction size, memory, and proxy limits.
|
||||
- Verify Vault isolation and failure rollback through real service integration tests.
|
||||
|
||||
### Stage 2: Immutable object-pack experiment
|
||||
|
||||
- Define a versioned, independently encrypted Chunk-record pack format.
|
||||
- Implement catalogue deltas and a local derived catalogue.
|
||||
- Compare S3-compatible range reads with WebDAV whole-pack fallback.
|
||||
- Validate bounded request behaviour without per-Chunk `HEAD` or `GET` loops.
|
||||
|
||||
### Stage 3: Snapshot and compaction
|
||||
|
||||
- Add catalogue snapshots, retirement records, fencing, grace conditions, and recovery.
|
||||
- Implement conservative repacking without Metadata rewrites.
|
||||
- Exercise concurrent writers and interrupted compactors.
|
||||
|
||||
### Stage 4: Consumer integration
|
||||
|
||||
- Extend Chunk retrieval with an explicit Adaptive Journal producer and terminal-condition contract.
|
||||
- Add multi-device Commonlib integration, CLI consumption, and focused real-Obsidian tests.
|
||||
- Define opt-in migration, rollback, diagnostics, and maintenance controls.
|
||||
|
||||
### Stage 5: Release decision
|
||||
|
||||
- Compare the adaptive paths with the current opaque Journal baseline.
|
||||
- Review privacy, corruption recovery, storage amplification, and operational complexity.
|
||||
- Select supported strategies and thresholds only after the evidence is available.
|
||||
|
||||
## Test obligations
|
||||
|
||||
- Metadata is never committed before all newly required Chunks have a durable source.
|
||||
- One logical Chunk ID never resolves to different accepted plaintext bytes.
|
||||
- Batch retry, duplicate physical storage, and concurrent publication are idempotent.
|
||||
- Native CAS batch limits are enforced by count and bytes.
|
||||
- Object-pack retrieval groups requests by pack and avoids per-Chunk network loops.
|
||||
- Range and whole-pack retrieval produce identical validated Chunk results.
|
||||
- Catalogue reconstruction from snapshots and deltas is deterministic.
|
||||
- A stale snapshot hint cannot hide later valid deltas.
|
||||
- Interrupted snapshotting, repacking, retirement, and deletion preserve readable data.
|
||||
- Every protected Metadata winner and live conflict branch remains reconstructible.
|
||||
- Direct Chunk delivery integrates with the maintained waiting and finite-activity boundaries.
|
||||
- Vault-scoped access and opaque remote Chunk identity prevent cross-Vault retrieval.
|
||||
- Mixed protocol versions fail closed before publishing incompatible Metadata.
|
||||
|
||||
## Open decisions
|
||||
|
||||
- Which Vault secret, derivation, and rotation contract should produce remote Chunk keys?
|
||||
- Is the object-store catalogue encrypted, and which non-secret header remains listable?
|
||||
- What exact pack framing supports independent authentication and efficient range reads?
|
||||
- Should a small urgent publication always be a micro-pack, or may it use a loose immutable Chunk object?
|
||||
- How are catalogue delta frontiers represented without relying on wall-clock ordering?
|
||||
- Which conditional-write, lease, or fencing mechanisms form the portable minimum across S3-compatible storage and
|
||||
WebDAV?
|
||||
- What reachability and retention policy protects non-live revision history?
|
||||
- What payload and response framing lets PostgREST stream large bounded batches safely?
|
||||
- When does PostgreSQL `bytea` remain appropriate, and when should a PostgREST deployment use an external object store?
|
||||
- Which benchmark results justify adaptive thresholds rather than one fixed strategy per remote?
|
||||
|
||||
These decisions remain Planning work. No implementation stage should silently choose them through incidental adapter
|
||||
behaviour.
|
||||
Reference in New Issue
Block a user