14 KiB
Architectural Decision Record: Gate Adaptive WebDAV with an Endpoint Safety Check
Status
Proposed as an experimental provider after the S3 Adaptive path has completed adapter, CLI, host, and real-host acceptance.
Context
WebDAV exposes an object-shaped interface suitable for immutable Commit Bundles and Packs, but method support and
conditional semantics vary across servers, gateways, reverse proxies, and authentication layers. A server can
advertise WebDAV while failing binary fidelity, replacing an object despite If-None-Match: *, returning incomplete
listings, or ignoring Range.
The common protocol decision is recorded in
Adaptive Journal as an explicit protocol. The Pack background, flat object
mapping, and detailed safety-check sequence are specified in the
Adaptive Journal Sync design. Commonlib owns the implemented
commit-bundle-v1 wire contract.
Decision
Adaptive WebDAV uses the same immutable Commit Bundle and external Pack semantics proven by S3, with a WebDAV-specific
flat object mapping inside the configured collection. Its Catalogue is derived locally from authenticated routes in
received Bundles rather than stored as a separate mutable remote object. Logical writer ordering comes from the host
ID, writer epoch, and dense sequence embedded in authenticated records. Correctness does not depend on a
server-provided startAfter order.
Before a new Adaptive repository becomes writable, a non-destructive endpoint safety checker exercises random reserved probe keys and reports observed semantic capabilities:
- binary write and exact read-back;
- read-after-write visibility;
- complete collection listing for the probe keys;
If-None-Match: *preventing replacement;- delete visibility; and
- exact byte-range behaviour, reported separately as optional.
The checker is an implementation acceptance target for compatible servers, not a claim that every WebDAV server must support Adaptive Journal. It touches no repository objects, attempts to remove every probe, reports incomplete cleanup, and never interprets authentication, permission, timeout, malformed response, or server failure as absence.
Conditional create, binary fidelity, complete listing, read-after-write visibility, and delete visibility are required. Range remains optional. The user selects whole-pack or Range retrieval based on their endpoint and own latency and throughput preference; the checker does not benchmark or recommend a policy.
Successful mutations do not receive unconditional confirmation requests. An ambiguous response is classified as
verify-first, and the exact immutable key is read before retrying. The existing opaque WebDAV layout and Adaptive
layout are detected separately; a mismatch requires a remote rebuild or another namespace.
Working hypotheses and illustrative estimates
The following values are planning assumptions for the first WebDAV experiment. They are not benchmark results, a provider-pricing statement, or a performance guarantee. The implementation must record the corresponding measurements before this ADR presents either Journal format as generally faster or smaller.
Cost and lifecycle assumptions
The initial deployment model is a self-hosted or quota-priced WebDAV service without a direct per-request tariff. Request count still matters because every request consumes a round trip, server work, connection capacity, and a share of any rate limit. Commercial services, gateways, and managed hosting may use a different charging model, so the adapter must not infer cost policy from the WebDAV label.
Storage capacity is expected to be the more visible constraint. RFC 4331 defines optional
DAV:quota-used-bytes and DAV:quota-available-bytes properties for
collections. When both are available, a connection diagnostic may report them as server-supplied evidence. Their
absence means 'unknown', not 'unlimited', and quota reporting is not an Adaptive safety requirement.
The first experiment has no ordinary remote Garbage Collection or repacking guarantee. Commit Bundles and external Packs remain immutable, unreachable data may remain retained, and an interrupted publication may leave an unreferenced Pack. A remote Rebuild is therefore the only guaranteed way to reclaim all experimental history. This makes retained bytes and object count acceptance measurements rather than later operational details.
Adaptive and Opaque Journal consume the same local PouchDB Chunk split. A smaller average changed Chunk can reduce retained history in either format. Adaptive adds authenticated routes and independently framed records, while Opaque adds its own container, compression, and encryption overhead. No comparative storage saving is assumed before both representations are measured with the same changes.
Ordinary request model
The object-profile model is shared with S3. Let:
Bbe the number of Metadata batches published as distinct Commit Bundles;Xbe the number of new external Pack objects needed by those batches;Jbe the number of Opaque Journal objects produced for the same changes;Wbe the number of visible Writer streams;Kbe the number of those Writer descriptors not already cached by the opened process;Pbe the number of additional complete Pack-container reads needed for missing Chunks; andMbe the number of missing Chunk frames fetched with Range.
After the endpoint safety result and repository binding have been cached for the active configuration, ordinary
publication performs approximately J Opaque PUTs or B + X Adaptive PUTs. Every Adaptive batch creates one Bundle;
each external Pack adds one preceding PUT. A confirmed conditional create does not add a read-back. An ambiguous PUT
adds one exact-key GET before any retry and remains outside the successful-first-attempt estimate.
The generic object-store receive path performs approximately:
| Retrieval policy | Requests without WebDAV listing reuse |
|---|---|
whole-pack |
1 + W + K + B + P |
range |
1 + W + K + B + M |
The 1 + W term represents one Writer listing and one Commit listing per Writer. A flat WebDAV collection normally
cannot apply those logical prefixes on the server: each call may require another Depth-one PROPFIND over the same
collection. The WebDAV target is therefore one complete listing at the start of a receive phase, filtered locally for
every Writer and Commit prefix in that phase. This receive-phase listing must expire before the next receive phase; it
must never become an unbounded cross-synchronisation cache. A Commit which becomes visible after the listing response
is handled by the next receive phase, which is consistent with immutable eventual replication.
With that explicitly scoped reuse, the request estimates become 1 + K + B + P and 1 + K + B + M. For 100 new
inline-Pack Bundles which require no additional Pack read, the difference is illustrative rather than a benchmark:
| Visible Writers | Descriptor state | Repeated-prefix listing | One receive-phase listing |
|---|---|---|---|
| 1 | first receive (K = 1) |
103 | 102 |
| 1 | warm process (K = 0) |
102 | 101 |
| 10 | first receive (K = 10) |
121 | 111 |
| 10 | warm process (K = 0) |
111 | 101 |
Opaque catch-up is approximately one listing plus one GET per new Journal object, or 1 + J. Holding B = J = 100
would therefore give 101 operations. That comparison does not assume that the two batchers produce equal object counts
for a real editing history.
Listing-volume hypothesis
Request count alone understates WebDAV discovery cost. Let N be the number of resources in the configured flat
collection and L the average XML response bytes per resource for a minimal Depth-one PROPFIND. Repeating the
listing for each logical prefix transfers roughly (1 + W) * N * L; one receive-phase listing transfers roughly
N * L.
Assuming L = 1 KiB only to make the scale visible gives:
Collection resources (N) |
One listing | Repeated listing, W = 1 |
Repeated listing, W = 10 |
|---|---|---|---|
| 1,000 | about 1 MiB | about 2 MiB | about 11 MiB |
| 10,000 | about 10 MiB | about 20 MiB | about 107 MiB |
| 100,000 | about 98 MiB | about 195 MiB | about 1.05 GiB |
Actual XML size, selected properties, URL length, compression, server implementation, and collection scope can change these values materially. The adapter should request only the properties it needs and measure both response bytes and wall time. The table explains the reuse target; it is not an estimate of every WebDAV server.
Ignoring cleaned probe resources and abandoned Packs, the current Commit Bundle layout contains approximately
1 + W + B + X remote objects: one manifest, one descriptor per Writer, one object per Bundle, and one per external
Pack. Object count therefore grows with synchronisation batches even when the retained payload is small, and it feeds
back into subsequent PROPFIND cost.
Retained-byte hypothesis
For a localised edit history, let E be the number of separately published changes and D the average bytes of newly
created encoded Chunk frames per change. Before Garbage Collection or Rebuild, retained Chunk bytes grow approximately
as E * D, plus Metadata, Commit, route, frame, server-metadata, and abandoned-publication overhead.
For a 10 MiB incompressible binary file whose content-defined split resumes after one changed Chunk:
Published changes (E) |
Average changed Chunk | Approximate new Chunk bytes retained | New Bundle objects when each Pack stays inline |
|---|---|---|---|
| 100 | 1 MiB | 100 MiB | 100 |
| 1,000 | 1 MiB | about 0.98 GiB | 1,000 |
| 100 | 256 KiB | 25 MiB | 100 |
| 1,000 | 256 KiB | 250 MiB | 1,000 |
These figures exclude Metadata and protocol overhead and do not predict text splitting. They show why a 10 MiB live file can consume much more than 10 MiB remotely after many committed edits. They also show why reducing Chunk size is a storage-versus-record-count decision shared with Opaque Journal, not an Adaptive-only recommendation.
Pack-target hypothesis
Changing the external Pack target does not normally duplicate Chunk frames: a Pack is their concatenation without capacity padding. Smaller targets chiefly increase object count, route data, PUTs, listing work, and later whole-Pack GETs. Larger targets increase peak buffer size, retry cost, transfer duration, and exposure to mobile watchdog or proxy timeouts.
For about 100 MiB of newly required encoded Chunk frames in one batch, the following serial-transfer model ignores RTT, TLS setup, encryption, server processing, contention, and retries:
| Pack target | External Packs (X) |
PUTs including the Bundle | One target-sized upload at 5 / 10 Mbit/s |
|---|---|---|---|
| 8 MiB | 13 | 14 | about 13.4 / 6.7 seconds |
| 16 MiB | 7 | 8 | about 26.8 / 13.4 seconds |
| 32 MiB | 4 | 5 | about 53.7 / 26.8 seconds |
| 64 MiB | 2 | 3 | about 107.4 / 53.7 seconds |
The total Chunk payload remains about 100 MiB in each row. The initial WebDAV experiment therefore retains the current 32 MiB preferred target as a compromise, not as a universal optimum or a user-facing recommendation. Measurements on the disposable server must record peak memory, per-PUT duration, retry behaviour, total object count, and watchdog survival. A later decision may lower the internal target for timeout-constrained endpoints without changing the wire format.
Staged acceptance
Adapter and Commonlib integration
Unit tests own HTTP status classification, conditional-create verification, flat-name round trips, complete listing, probe isolation, cleanup reporting, Range validation, and whole-pack fallback. A disposable WebDAV integration runs the safety checker before exercising Adaptive Metadata and Chunk synchronisation. The integration also proves that one receive phase reuses a single collection listing, then invalidates it so the next phase can observe a new Commit.
CLI end-to-end acceptance
The built CLI applies an Adaptive WebDAV Setup URI to a second independent database and synchronises text and binary Chunk-backed files through a real disposable server. One client uses whole-pack retrieval; Range is added to this layer only when the selected test server proves it. The CLI test does not repeat the complete endpoint capability matrix.
Host settings and UI
The WebDAV dialogue exposes Adaptive mode only with clear capability-check results. It persists the expected repository ID and the selected retrieval policy, defaults to whole-pack, and reports that Range is optional. Focused tests own profile and Setup URI preservation without contacting a server.
Real-host end-to-end acceptance
One real-Obsidian workflow uses the same known disposable WebDAV implementation, runs the safety gate, and proves a representative Chunk-backed transfer. Servers outside that fixture are diagnosed by the checker rather than added to a large real-host matrix.
Alternatives rejected
Trust advertised WebDAV methods
Method advertisement does not prove the conditional, listing, visibility, and byte semantics required for immutable publication.
Treat Range as mandatory
Whole-pack retrieval is correct and often competitive for throughput. Requiring Range would exclude otherwise safe servers for an optional optimisation.
Add server-specific compatibility branches
The remote may be any implementation or proxy composition. Semantic checks produce a maintainable contract, while a growing server-name table would remain incomplete and become stale.
Consequences
- WebDAV remains experimental because suitability is endpoint-specific.
- The safety checker gives a concrete reason when a server cannot host Adaptive Journal.
- Common object-pack behaviour is inherited from the earlier S3 boundary, keeping WebDAV-specific tests focused on HTTP semantics and name mapping.
- Optional Range support can improve request efficiency without becoming a data-availability requirement.