Files
alkfs/docs/research/phase-0.md
T
glm-5.3-flash 23a39cc9c0 Phase 0 second pass: storage identity + two-crate split + anchor workload
- Reframe storage identity: git-compatible sha1/sha256 (algorithm-tagged),
  not BLAKE3 — the ancestor POCs' BLAKE3 was an iroh-blobs artifact, and
  consuming iroh-blobs (OQ-FS-03 option (a)) dissolves with it; findings
  transfer (inline/outboard hybrid, GC shape, write-ordering analysis),
  code doesn't
- Chunk-level dedup adopted as working goal for large content, with the
  honest calibration recorded: git dedups at object granularity (objects
  are effectively single-chunk); chunk manifests are the generalization
  that pays on workspace-scale files
- Two-crate working direction: alkblobs (bucket-style CAS blob store,
  appfile layout) beneath alkfs (path tree + branches + serving protocol);
  durability contract now crosses the put/get seam
- New OQ-FS-16: remote-mount anchor workload — today's sshfs/vanilla-SFTP
  mount of this dev server's workspace, with an honest decomposition of
  why it's slow and what an indexed VFS fixes server-side
- POC register: #3 rewritten as alkblobs sha1/sha256 store skeleton,
  #4 reframed (git objects ride whole-file), new #8 mount-workload
  baseline (sshfs latency measurement, runnable today)
- AGENTS.md: two-crate direction in the crate description + convention 10
  (identity, chunk-granularity note, seam durability contract) + a
  Phase-0 working-direction quick-reference section (leanings, not
  decisions)

Verification: docs-only repo — all 16 OQ ids contiguous, every referenced
workspace path exists, BLAKE3 references re-checked as historical context
2026-09-23 09:19:58 +00:00

915 lines
52 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
status: draft
last_updated: 2026-09-23 (second pass, same day — storage identity
reframed to git-compatible sha1/sha256: the ancestor POCs' BLAKE3 was an
iroh-blobs artifact, not a choice; chunk-level dedup adopted as the
working goal; alkblobs/alkfs two-crate split added as the working
direction; the remote-mount SFTP use case recorded as the anchor
workload. Still unconverged — every OQ is open, the POC register holds
proposals not results)
---
# alkfs — Phase 0 (Exploration)
This document captures Phase 0 (Exploration) for the `alkfs` crate: vision,
guiding principles, prior art, open questions (OQ-FS-01..NN), and the
candidate POC register. Phase 0's objective per `docs/sdd_process.md`:
*capture vision and guiding principles; research options; validate
approaches; converge on a recommended approach.* It is the input to Phase 1
(Architecture), where the Architect will produce `docs/architecture/`
specs, ADRs, and the open-questions tracker.
Drafted 2026-09-23 from the initial setup discussion, revised the same
day as the storage-identity reframe landed (§Working direction). The
crate is the storage-engine member of the alk* family: alkcall (call +
channels — the substrate), alktty/alktunnels/alksocks (the
protocol-crate siblings), alkgit (the first in-family storage consumer,
currently paused on exactly the problem this crate exists to solve).
**Honest framing:** this Phase 0 is earlier in its lifecycle than
alksocks' was at the equivalent commit. There are more unknowns here than
in any sibling so far — a filesystem is both a storage engine (hashing,
chunking, durability, GC) and a serving protocol (verbs, sessions, wire
framing), and almost none of those decisions have empirical ground yet.
The POC register below is proposals, not results. Expect this document to
be rewritten several times before convergence.
## Vision and guiding principles
**One sentence:** a content-addressed, branch-aware virtual filesystem for
the alk family — a local storage engine (path-tree metadata +
content-addressed blobs) with a producer/consumer serving protocol on
alkcall channels, so that alkgit's object storage, the coming alksftp
door, and the alknet filesystem vision all compose on the same substrate —
and it never touches the host filesystem unless explicitly configured to.
**Working direction (2026-09-23, from the setup discussion — leanings to
converge, not decisions):**
- **Git-compatible identity, not BLAKE3.** The ancestor POCs used
BLAKE3 because iroh-blobs does; flipping the causality, the blob
layer should carry the appfile concept with the hashes git uses
(sha1/sha256). This dissolves the "BLAKE3 must hash the whole file
before the address exists" problem into a *chosen* design (OQ-FS-04),
and makes git object identity native rather than mapped.
- **Chunk-level dedup is the working goal** for large content
(workspace-scale files, datasets, logs). Whole-file hashing dedups
only identical files; git itself dedups at object granularity (packs
delta-compress within a pack, but the address space is per-object);
chunking is the storage-layer generalization (OQ-FS-04 — measure
whether the workloads pay before committing).
- **The two-crate split: `alkblobs` + `alkfs`.** A bucket-style
content-addressed blob store (the S3 shape) beneath the path-tree
mapping (the alknet-filesystem POC's layer 2). alkgit can consume the
blob store without a filesystem; the FS layer keeps its own crate
(OQ-FS-01).
- **The anchor workload is remote mounting.** The concrete use case:
the dev-server workspace mounted on a local Linux machine under
`remote/` — today over vanilla SFTP (sshfs), which is slow in known
ways (a round trip per syscall, stat storms, full inode work per
stat). An appfile-indexed VFS fixes the server side; caching and
pipelining close the rest (OQ-FS-16).
**Why this crate exists.** The alknet mono-repo's filesystem research
(three POC iterations, 24 passing tests) proved a viable three-layer
architecture but lived inside a project with excessive scope. The alk*
family has since been decomposed and published (alkcall, alktty,
alktunnels, alkvault; alksocks and alkgit in flight). alkgit paused
because its storage problem — content-addressed blob storage with
branch/CAS semantics — is not a git-specific problem, and solving it
inside alkgit would leave alksftp and the alknet filesystem rewrite to
solve it again. The same basic "shape" recurs across the family:
git + git-lfs is small-object CAS metadata over big-blob content storage;
iroh-blobs' FsStore is inline-small/outboard-large; the alknet-filesystem
POC is SQLite path edges over a blob store. alkfs is that shape, made a
crate.
**Scope sketch (unconverged; restructured 2026-09-23 around the
two-crate working direction — Phase 0 must confirm or cut, OQ-FS-01):**
1. **alkblobs (candidate companion crate)** — bucket-style
content-addressed blob storage (the S3/rudolfs shape): put/get/has/
delete by hash, tags/pins for liveness, GC, the appfile layout (one
transactional store for blob metadata + inline small content;
file-backed spill-over for large content), git-compatible sha1/
sha256 identity, chunk-level dedup for large content. Fully local,
embeddable in-process, no channels required; engine-only in v1 (no
serving ALPN of its own yet — OQ-FS-09).
2. **alkfs (this crate)** — the path-tree mapping on top (the POC's
layer 2): paths, branches, snapshots, tombstones, cached sizes,
write sessions (branch-on-write/merge-on-close); plus the
producer/consumer serving protocol on alkcall channels — a producer
registers openable filesystem channels
(`ChannelCore::register_openable`); a consumer opens them and speaks
the file protocol inside. The `alk/fs`-shaped ALPN resource, the
"ALPN as a service" shape of alktty/alktunnels/alksocks. Depends on
alkblobs, or a narrow blob seam if the split puts a trait there
(OQ-FS-01).
3. **Doors stay out** — alksftp (SFTP door), FUSE/mount adapters, sync
tools are separate crates/features that consume alkfs, per the family
rule (alkgit vision: doors are family infrastructure, not crate
contents).
Guiding principles, inherited from the alk* family plus the filesystem
research:
1. **No forced host-FS access — the defining requirement.** The analogue
of alksocks' "never binds a port": the engine manages its own content
store and never reads, writes, or mounts the host filesystem unless
explicitly configured to. Every bridge to the real world (a blob-file
store, an SFTP door, a mount, a sync tool) is an explicit, optional,
feature-gated assembly shape. The engine itself is storage-agnostic
about what backs its blob layer (filesystem files, SQLite, remote).
2. **Metadata/content split is load-bearing.** Small structured state
(path edges, branch pointers, tombstones, sizes) lives in a
transactional store; content bytes live under their hash in the blob
layer. Path operations are O(path edges), never O(bytes) — rename is
O(1) regardless of file size; identical content dedups across
branches and paths by construction (and, with chunking, dedups at
chunk granularity). This is the alknet-filesystem POC's core
conclusion and the same shape as git + git-lfs and iroh-blobs'
inline/outboard split — with git-compatible identity instead of
BLAKE3.
3. **Content addressing with a durability contract.** Content is
identified by its hash, not its path — with the hashes git uses
(sha1/sha256), so git object identity is native to the store, not
mapped. The invariant that must survive every design choice: content
is durable before any metadata row naming it commits; a crash
mid-write leaves the old version intact and visible; no torn version
is ever readable; no metadata commit orphans its content. With the
two-crate direction the contract crosses the put/get seam (alkfs
must be able to trust that an alkblobs `put` returning means
durable, or get an explicit durability level back) — the seam
contract is an OQ. Exact hashing/chunking is an OQ; the invariant
is not.
4. **Branch-aware from day one.** Fossil-style branches (a branch is a
parent chain + per-branch overrides + tombstones), branch-on-write/
merge-on-close for the write path. This is what gives free
multi-agent/multi-fork content sharing and the POSIX
concurrent-reader-sees-old-version property — and it is the property
git's object model needs (content shared across refs by hash).
5. **"ALPN as a service," not "a server."** The filesystem is a produced,
ACL-scoped resource on a channels connection. The protocol never
opens host files or binds anything; door-shaped conveniences (local
loopback front door, in-process consumer handle) are optional
assembly-layer shapes on either side. Producer/consumer vocabulary,
never server/client.
6. **Substrate-agnostic protocol, native engine.** The file-protocol
state machine is generic over `T: AsyncRead + AsyncWrite + Unpin`
(fast-socks5/alksocks genericity precedent); the storage engine is
native-only behind features (real FS I/O, SQLite). Whether the
protocol layer itself stays wasm-clean is an OQ, not an assumption.
7. **Multi-tenancy is a where-clause, not an afterthought.** The POC's
`bucket` column is the isolation unit; identity → bucket mapping is
an assembly-layer auth question; per-path/per-bucket policy is a
Phase 1 design question. A filesystem is arbitrary read/write by
nature — the open gate and path-scope policy are the security
boundary.
8. **Bounded resources on every serving path.** alkgit's ADR-009 posture
(wall-clock, size, round limits) is the consumer shape this crate
must serve; the serving protocol inherits alkcall's backpressure and
channel-cap invariants rather than re-deriving them.
## What is already settled
The foundation is POC-validated and (upstream) ADR-pinned; this crate
does not start from zero. It inherits:
- **The channels substrate** — demux→Connection→handler→mux, the
establishment story (`register_openable_with_establisher`, alkcall
ADR-049; refused sessions are typed `channel:open_failed` call errors,
never phantom channels), the identity seam (CF-005/CF-006: per-call
opener identity on the open-op hooks), ACL-for-free via
`register_openable`, the two-pump data plane (`channels::pump_bidi`,
ADR-050 — use it, do not hand-roll), hub relay
(`ChannelRelay`/`HubLegTemplate`, ADR-051). All production-validated by
alktty/alktunnels/alksocks.
- **The metadata/content split, POC-proven.** alknet-filesystem POC
iteration 1: SQLite path tree over a blob store, 8 tests — branch
inheritance, tombstones, O(1) rename, bucket isolation, honker
notify-atomic-with-mutation.
- **The write path, POC-proven.** Iteration 2: branch-on-write/
merge-on-close reconciles "BLAKE3 must hash the complete file" with
"writes arrive in chunks, possibly out of order" — concurrent readers
see the old version until close() commits atomically; crash/abort
leaves the old version intact. 7 tests.
- **The distributed-sync shape, POC-proven but scope-uncommitted.**
Iteration 3: path tree as an automerge CRDT synced over QUIC; 9 tests;
metadata and content sync separately; concurrent-root-map
initialization is a real constraint (root structures must be created
by one node and synced before others write). Whether any of this is
alkfs v1 scope is OQ-FS-14.
- **The iroh-blobs store-seam findings (the external-store probe).** The
alknet-blobs probe traced iroh-blobs 0.103's full `Command` actor
surface: there is **no `Store` trait** — `Store` is a concrete struct
over an `irpc::Client`; `MemStore`/`FsStore` are actors; a third store
is a third actor speaking the pub `Command` enum. Blockers are a
~4-line visibility PR (`Store::from_sender`, `ref_from_sender`,
`ApiClient`, the `Scope` field); GC (`run_gc`) is pub and reusable;
~120-160 lines of bao-tree glue must be re-implemented per store. The
probe's cautionary half: `TempTags`/`TempTagScope` are sealed
`pub(crate)` — sealed surface is exactly what turns "external store"
into "fork" (AGENTS.md convention 13's cautionary example).
- **The consumer shapes this crate must serve.** alkgit's backend trait
family (`docs/architecture/backend.md` there: `GitRefs`, `GitPackGen`,
`GitPackIngest` — streaming pack generation/ingestion, CAS ref
transactions, budgeted resources) and the russh-sftp `Handler` trait
(the POC's near-1:1 SFTP↔PathTree mapping table). "Optimize for git
and sftp-like" is the working posture.
- **The SQLite-as-application-file-format insight.** BLOBs < ~100KB are
faster inline in SQLite than as filesystem files; atomic transactions
over path-tree metadata; the schema is the documentation. iroh-blobs'
FsStore discovered the same shape independently (redb inline tables +
filesystem files). The inline-small/outboard-large hybrid is the
metadata/content split at storage granularity.
- **The family conventions** — AGENTS.md in this repo (error handling,
tokio subset, feature-gating, upstream posture, one-way doors) and the
alkcall ADR set listed there.
## Prior art
### alknet-filesystem POCs — the direct ancestor
`/workspace/@alkdev/alknet/docs/research/alknet-filesystem/`
(`poc-summary.md`, `alknet-blobs-external-store-probe.md`), POC crates at
`/workspace/alknet-filesystem-poc` and `/workspace/alknet-fs-sync-poc`.
The three-layer conclusion (SQLite path tree + iroh-blobs content +
honker coordination) and the write-path resolution
(branch-on-write/merge-on-close) are this crate's inheritance, not prior
art to re-litigate — but *every dependency choice* in those POCs is open
again:
- **SQLite path tree** (`rusqlite` 0.39, bundled): `buckets`/`branches`/
`paths`/`tombstones` + `write_sessions`/`write_chunks`; recursive-CTE
chain walk resolves `(bucket, branch, path)` → content link in
sub-millisecond time; only deltas are stored per branch.
- **iroh-blobs `MemStore`** as the content layer (deliberately not
`FsStore` — no redb, no fsync rabbit hole in the POC).
- **honker-core** for notify-in-transaction (the transactional-outbox
pattern built in): `SELECT notify(...)` inside the same transaction as
a path-tree mutation; watchers wake on commit, not poll.
- **What did NOT transfer and why** — the POC's remaining unknowns
(FsStore/redb coexistence, SFTP wiring, GC/tags, chain-depth perf,
snapshot semantics) are this Phase 0's OQ list; the probe resolved the
two-database question by reframing it (external actor, own SQLite
file), and the redb question dissolves entirely if alkfs owns its blob
metadata in its own store.
### iroh-blobs — the nearest CAS store (evaluated; NOT adopted, and the BLAKE3 causality)
`/workspace/iroh-blobs` (v0.100 checkout) + published 0.103. `DESIGN.md`
is the best available treatment of the blob-store trade space (files are
hard: the bitfield/data/outboard write-ordering problem). What it gives:
content addressing, bao-tree verified streaming (per-chunk integrity with
an outboard), dedup by construction, partial-range reads, tags +
mark-sweep GC, a hybrid store (small inline in redb, large as filesystem
files) — the same metadata/content split as principle 2.
**The causality correction (2026-09-23):** the ancestor POCs' BLAKE3
choice was an **iroh-blobs artifact** — BLAKE3 is load-bearing across the
whole iroh-blobs surface (`Hash` type, bao outboards, the `Command`
actor's payload types), so consuming iroh-blobs means inheriting BLAKE3
and its whole-file addressing. Flipping the causality — *we choose the
identity* — dissolves the "must have the full file for blake3" problem
into a design question (OQ-FS-04) and dissolves the "consume iroh-blobs
as a store" option (OQ-FS-03's option (a)) entirely: a sha1/sha256
identity layer over iroh-blobs' Command surface would be a mapping
shim on top of a foreign hash, not a store. What remains worth taking
from iroh-blobs is *findings*, not code: the inline/outboard hybrid
layout, the write-ordering analysis, the mark-sweep GC shape, and the
probe's actor-seam map (what a store API needs to expose to stay
consumable).
**The other frictions (record, for the research they hold):**
- **The transfer protocol is welded to iroh/QUIC.** iroh-blobs' network
story is an iroh `ProtocolHandler` speaking its own ALPN over QUIC. For
alkfs this is irrelevant-by-design — content transfer rides alkcall
channels — but it means iroh-blobs is at most a *local store*
dependency, never the transfer layer. (The store side itself is
transport-independent — that is what the Command-actor probe proved.)
- **Whole-file naming vs incremental writes.** BLAKE3/bao names a blob
by the root hash of its *complete* byte sequence; you cannot compute
the address until all bytes exist. Partial data can live under temp
tags (`BlobStatus::Partial`) but cannot be pinned by its final hash.
The POC's branch-on-write/merge-on-close is one reconciliation; chunk
lists / Merkle-DAG naming is the other (OQ-FS-04). git escapes the
problem structurally: objects are small and independently hashed;
packs are the exception, and pack *generation* streams to a consumer
that hashes on arrival.
- **Two-database coexistence** (redb for blob metadata + SQLite for the
path tree): two WALs, two fsync paths, two crash-recovery stories —
the POC summary's unknown #1, reframed by the probe ("neither fork nor
coexist: a third store actor with its own single SQLite file"), and
dissolvable entirely if alkfs owns blob metadata in its own store
(OQ-FS-02/03).
- **Sealed internals** (`TempTags`, the `BaoTreeSender`) — the probe's
fork-vs-PR calculus. Third-party upstream, not ours, never will be
(AGENTS.md convention 17).
### git + git-lfs — the shape to optimize for
The user-level framing this crate must honor: "the same basic shape
exists with git + gitlfs as with how iroh's blobs handled the mix of
small and large blobs." git itself is the small-object CAS: every object
individually hashed (sha1 today, sha256 in the object-format transition),
refs are names over hashes, content shared across refs/branches for free,
rename is metadata-only. git-lfs is the large-blob sidecar: content-
addressed pointer rows in the tree, bytes in a separate content store
(rudolfs is the family's reference server for that half — namespace/
bucket isolation, LRU cache decorator, fanout streaming). The alkfs
analogue: the metadata layer *is* git's object/ref shape; the blob layer
*is* the LFS store; small content can inline into the metadata store
exactly as iroh-blobs inlines small blobs into redb. With sha1/sha256
identity (the 2026-09-23 reframe), **git objects are natively addressable
in the blob layer** — no oids→hash mapping layer, no dedup seam between
git's CAS and the store's CAS.
**One honest calibration: git dedups at object granularity, not chunk
granularity.** Packs delta-compress similar objects *within* a pack, but
the address space is per-object. Chunk-level dedup (the working goal,
§Working direction) is the storage-layer generalization that pays on
workspace-scale files (datasets, logs, agent workspaces with large
append-only artifacts) — not on git objects, which are effectively
single-chunk. If alkblobs chunks large content, git's pack/idx regime
still rides above it as object-granular rows; the two granularities
compose (OQ-FS-04, OQ-FS-12). Whether alkgit keeps gix-odb native or
serves its object storage from alkfs/alkblobs is OQ-FS-12 — the
highest-stakes scope question in this document.
### alkgit — the first consumer
`/workspace/@alkdev/alkgit` — paused on the storage problem. Its backend
doc (`docs/architecture/backend.md`) pins the seam: five traits
(`GitRegistry`, `GitRegistryStore`, `GitRefs`, `GitPackGen`,
`GitPackIngest`), kept small/orthogonal/substrate-blind after the
alknet-blobs sealed-surface lesson. The properties alkfs must serve
efficiently: streaming pack generation (O(counts) memory, blocking-thread
friendly), streaming pack ingestion (budgeted, fsck/connectivity report),
CAS ref transactions, registry records with grants. Its research
(`docs/research/gitoxide.md`, `poc2-findings.md`) carries the gix-odb
API contract notes — including the durability shape (pack bytes durable
before the ref transaction that names them commits) that mirrors
principle 3 exactly.
### russh-sftp / alksftp — the door shape
`/workspace/russh-sftp` (read-only reference): the `Handler` trait is a
near-1:1 mirror of POSIX syscalls as SFTP packets, and the POC mapped
every op onto the PathTree + WriteSession API (open→resolve or
WriteSession::open; write→write_chunk with offset as key; close→hash+
merge+notify; readdir/list_dir; stat→indexed row — cheaper than a real
FS fstat; rename→O(1) edge move). The client's `File` already implements
`AsyncRead + AsyncSeek + AsyncWrite` with pipelined writes. alksftp (the
coming family crate) would speak this verb set; whether alkfs's serving
protocol adopts SFTP-shaped verbs natively or defines its own framing
that alksftp translates is OQ-FS-08. The alknet-tty async-IO adapter
patterns (AsyncWrite-over-mpsc, kill-on-drop) are reusable for the write
bridge.
### SQLite (application file format) + honker
https://sqlite.org/appfileformat.html — the insight that anchored the
POC. honker (`/workspace/honker`, `honker-core` 0.2.4) supplies
notify/locks/queues as SQL functions on *your* connection — the
transactional-outbox property. Caveats: honker is third-party (not ours,
convention 17), single-machine by explicit design ("two servers writing
the same .db over NFS is not a Honker deployment strategy"), and its
scope in alkfs is an OQ (notify-on-commit may be the only load-bearing
piece — locks/queues/scheduler may be excess).
### rudolfs — the git-lfs server reference
`/workspace/@alkdev/alknet/docs/research/references/gitlfs/
rudolfs-reference.md`: `StorageKey = (Namespace, Oid)` tenant isolation;
the decorator composition `Verify ↔ Encrypted ↔ Cached ↔ Retrying(Disk →
S3)`; LRU cache in front of permanent storage; `fanout()` streaming to
client and cache simultaneously. The cache/permanent split and the
fanout pattern are the production-shape references for alkfs's blob
layer and its serving read path.
### Anti-prior-art (what NOT to carry over)
- **The alknet mono-repo's scope.** The filesystem research is inherited
as *findings*, not as a code path; the crate stays small per the
decomposition discipline (alksocks' "socks + channels, nothing else"
resolution is the template — alkfs's version is OQ-FS-01).
- **iroh-blobs' transfer protocol as the content path.** Content
transfer rides alkcall channels, full stop; an iroh/QUIC side-protocol
would fork the family's transport story.
- **iroh-blobs' FsStore partial-file lifecycle as a requirement.** Its
749-line `bao_file.rs` partial-blob-on-filesystem machinery exists
because redb/filesystem is its substrate; a SQLite-inline store (POC
write_chunks) or an alkfs-owned blob store has no need for it.
- **gix-odb's layout as alkfs's layout.** Loose-objects + packfile
directory layout is git's on-disk contract; alkfs is a general
engine. If alkgit keeps gix-odb native, alkfs serves the general shape
and git storage stays in alkgit (OQ-FS-12).
- **NFS-style shared-file multi-node assumptions.** honker's own warning
applies to the whole engine: single-machine local state first;
distribution is an explicit layer (OQ-FS-14), not an ambient property.
## Open Questions
These are the design questions Phase 0 must resolve (or explicitly
defer) before the architecture spec. Numbered OQ-FS-01.. so they can be
referenced, tracked, and promoted into `docs/architecture/
open-questions.md` in Phase 1. Status is open unless stated; the point
of this document is to hold the reasoning without forcing premature
decisions. Grouped by theme; numbering is stable across reorganizations
(do not renumber — findings files and commits reference these ids).
### Core architecture
#### OQ-FS-01: Crate scope — one crate or the alkblobs/alkfs split?
**Working direction (2026-09-23): the two-crate split — `alkblobs` (the
bucket-style CAS blob store) + `alkfs` (the path-tree mapping + serving
protocol).** Rationale: a git-like blob store is a complete, useful
thing without a filesystem (alkgit could consume it directly; S3-bucket-
shaped usage exists on its own), the FS layer is a separate concern with
its own consumers, and the POC's layer separation (blob bridge as an
explicit module) already drew the seam. Open sub-questions:
- One repo or two? The family has both shapes (alkcall single-crate;
alknet's `crates/` workspace). Two crates in one repo (a workspace)
keeps the seam cheap to iterate until first publish; two repos match
the family's one-crate-per-repo posture but tax early co-development.
Working lean: one repo, two crates, until the seam stabilizes.
- **The seam shape**: does alkfs consume a *concrete* `alkblobs` (the
POC's blob_bridge pattern), or does a small blob trait live in alkfs
(or a tiny `alkblobs-types` leaf) so an embedder can substitute its
own store? The sealed-surface lesson
(`alknet-blobs-external-store-probe.md`) says decide before consumers
exist; the alkgit trait-family precedent (`docs/architecture/backend.md`
there) is the template for keeping it small. The put/get durability
contract (principle 3) rides this seam regardless.
- What is explicitly out: doors (alksftp, FUSE, mount), sync tooling,
UI/registry applications, the alknet rewrite's client stack.
#### OQ-FS-02: Metadata store substrate — and is blob metadata in the same file?
The POC used `rusqlite` (bundled) for the path tree and it worked well
(schema-as-documentation, recursive CTEs, BLOB columns at chunk
granularity). Open sub-questions:
- SQLite/rusqlite vs redb vs something else for the path tree. The POC
evidence favors SQLite (honker integration, transactional outbox,
sub-ms resolves); redb buys a lighter footprint but loses SQL and the
honker seam.
- The **one-file-vs-two question**: if alkfs owns the blob layer too,
blob metadata (hash index, sizes, refcounts/liveness) can live in the
*same* SQLite file as the path tree — one WAL, one commit boundary,
and the durability ordering of principle 3 becomes a single
transaction instead of a cross-store fsync dance. The probe's
external-actor shape (iroh-blobs with its own store) vs an alkfs-owned
store decides whether this is even on the table. Small content can
then inline as BLOBs (the appfileformat sweet spot) — possibly making
the "blob store" a layer *inside* one database with file-backed
spill-over for large content (the iroh-blobs FsStore hybrid shape).
- Concurrency model: single writer connection + reader pool? WAL
settings (`synchronous=NORMAL` vs `FULL`) as a durability-vs-throughput
knob? What is the honest fsync contract per operation class?
#### OQ-FS-03: Blob substrate — own the store (the direction) — what does alkblobs take from where?
**Reframed 2026-09-23: option (a) "consume iroh-blobs as a store" is
dissolved** — BLAKE3 is load-bearing across its entire surface (`Hash`
type, bao outboards, `Command` payload types), and the identity decision
has flipped to git-compatible sha1/sha256 (§Working direction). A
sha1/sha256 layer over iroh-blobs' Command surface would be a mapping
shim on a foreign hash, paying its dependency weight and its sealed-
surface risks to inherit a hash we don't want. **The working direction
is option (b): own the store (alkblobs).** What that leaves open:
- **What to take as findings vs build fresh**: iroh-blobs' inline/
outboard hybrid layout, write-ordering analysis, and mark-sweep GC
shape transfer as *findings* (§Prior art); the `bao_tree` crate
(BLAKE3-keyed) does not transfer for verified streaming — if
per-chunk integrity is wanted under sha1/sha256 identity, the chunk
index itself becomes the verification structure (each chunk row
carries its own hash; reads re-verify per chunk), which is a simpler
design than outboard files and falls out of the chunk manifest
(OQ-FS-04) for free.
- **The appfile layout** (OQ-FS-02's one-file question, now scoped to
alkblobs): one transactional store for blob metadata + inline small
content, file-backed spill-over for large content, single commit
boundary for the durability contract.
- **Bucket semantics**: the S3/rudolfs shape — namespace/bucket as the
isolation + naming prefix. How buckets relate to the alkfs layer
(alkfs buckets = alkblobs buckets? a mapping table? one alkblobs
store per alkfs bucket?) is an OQ-FS-11 sub-question; multi-tenancy
at the blob layer is where-clause isolation per the POC.
- **sha1 vs sha256 vs both**: git's dual-object-format reality (sha1
repos today, sha256 transition) says the store should carry
algorithm-tagged identities (a small enum per hash) rather than
hard-coding either — git object ids are natively addressable then
(OQ-FS-12's coexistence sub-question mostly dissolves into this).
- **Versioned-hash hazard worth recording**: sha1/sha256 are *not*
length-extended-safe for streaming concatenation the way tree hashes
are — any chunk-manifest scheme must be its own construction (hash
list / Merkle over chunk hashes, e.g. the git tree-object shape
itself), not a naive linear hash chain. This is a design constraint,
not a blocker.
### Write path, branching, GC
#### OQ-FS-04: Hashing and chunking — object-granular identity over a chunk manifest
**Reframed 2026-09-23.** The identity half is decided in direction:
**sha1/sha256, algorithm-tagged** (OQ-FS-03) — git-compatible, native to
alkgit. What remains open is the *dedup granularity* and the manifest
shape — the load-bearing storage-format one-way door once content
exists in the wild:
- **Whole-file sha256 (the git shape).** Objects are addressed whole;
identical files dedup, similar-but-not-identical do not. Simplest,
matches git objects exactly, and matches the POC's tested write path
verbatim. For large workspace content the dedup miss is real
(two 1GB files sharing a 500MB prefix dedup zero bytes).
- **Chunk manifest (the working goal for large content).** A file is a
root manifest over chunk hashes (a hash list — the git tree-object
shape generalized, per OQ-FS-03's versioned-hash constraint); chunks
dedup across files, partial availability and resumable writes come
along, and per-chunk re-verification on read falls out of the
manifest (each row carries its own hash — the bao-outboard role,
without bao). Chunker choice (CDC vs fixed-size) and boundary-shift
sensitivity are the design cost.
- **Hybrid (the probable landing):** whole-file identity under a
size threshold (git objects, source files — effectively single-chunk
anyway); chunk manifests for large content (datasets, logs, pack-
scale blobs). The threshold is a knob; the manifest format for the
chunked side is the one-way door.
Sub-questions: CDC (rollsum/buzhash) vs fixed-size vs git-style
delta-encoding as *adjacent* dedup (packs delta-compress similar
objects — a chunk-manifest store could optionally delta-compress chunk
rows, but that is a compression layer, not identity); where chunk
indices live (alkblobs manifest tables); whether cross-*bucket* chunk
dedup is ever allowed (working answer: never — tenants are isolated,
OQ-FS-11); interaction with OQ-FS-05 (a chunk-manifest write can commit
incrementally, weakening the branch-on-write staging argument for
chunked files — the session model may collapse to "extend the manifest"
for large content).
#### OQ-FS-05: Write path semantics beyond the POC
Branch-on-write/merge-on-close is POC-proven for the happy path. What
Phase 0 must still answer:
- **Durability details** — per-chunk transaction commit (POC shape) vs
batched WAL; `synchronous` level; whether fsync of spilled large
content must precede the path-row commit (principle 3) and how that
is ordered when content lives outside the DB file; crash-mid-close
behavior.
- **Concurrency** — the POC left honker named locks unwired; concurrent
writers to the same path need a real policy (last-close-wins is
session-safe but not POSIX; O_EXCL-ish create semantics; advisory
byte-range locks for the sftp door?).
- **Streaming large writes** — pack generation and 1GB+ uploads: the
POC's per-chunk SQLite rows are proven at ~32KB×1MB; the pack-size
regime needs a spill story (OQ-FS-02's file-backed overflow) and a
backpressure story inherited from alkcall channels.
- **Sparse/out-of-order/ftruncate** — SFTP pipelines out-of-order
(proven); sparse files and explicit truncation/extend are
unaddressed.
#### OQ-FS-06: Branch/snapshot/commit model
The POC has Fossil-style branches (name → parent chain + deltas) but no
explicit commit/snapshot op — writes to a branch are immediately visible
on that branch. Open questions:
- Is a snapshot a new branch (Fossil's model — maps naturally to the
chain walk) or a recorded point-in-time within a branch (git's model)?
What do alkgit (refs + CAS) and the sftp door each need?
- Refs as a first-class concept: alkfs stores `refs` rows naming commits
(branch heads), enabling CAS ref transactions (alkgit's
`GitRefs::apply` shape) over the same metadata store. Is the ref
namespace git-shaped from day one, or a later addition?
- Merge semantics across branches: the POC has none (branch-on-write
collapses trivially at close). Real merges (three-way? automerge-CRDT
for the metadata layer per POC 3?) are presumably out of v1 — say so
explicitly rather than leaving it implied.
- Chain-depth performance: the recursive CTE degrades on deep chains
(POC unknown #6); resolve-cache/materialized-view shape if needed —
perf probe before spec, or accept and document.
#### OQ-FS-07: GC and liveness
Content is shared across branches/paths by hash, so naive per-path tags
(Poc unknown #5's iroh-tags shape) break sharing — deleting a path must
not collect content another path/branch still names. The candidate
shape: **liveness = reachability walk** (mark from all live branch heads
+ refs + write sessions; sweep unreachable) — a metadata-store query
over path rows, no separate tag table needed if blob liveness rows are
refcounted or mark-computed at GC time. Sub-questions: incremental vs
stop-the-world GC; tombstone retention (content behind tombstones is
unreachable-but-recent — grace windows); orphaned write-session chunk
reaping (crash cleanup, the POC's known orphans); cross-bucket sharing
(never — buckets are tenants).
### Serving protocol
#### OQ-FS-08: The file protocol over channels — verbs, sessions, framing
The biggest unspecced surface, and the crate's first real wire format
(one-way door — ADR before the first consumer):
- **Verb set.** SFTP-shaped (the russh-sftp Handler mapping is proven
1:1 against the engine) vs a leaner custom set (open/read/write/
close/stat/readdir/rename/remove + extended ops). Working posture:
SFTP-shaped verbs, SFTP-versioned semantics where it matters
(handles, pipelined writes, `SeekFrom::End` cheapness).
- **Session model.** One channel per file session (alksocks' one-channel-
per-CONNECT shape) vs one control channel multiplexing many file
handles (the alktty demux shape). A directory traversal or a git clone
opens *many* files; per-file channels spend channel IDs and opens
(the 256-cap arithmetic, OQ-SK-09's twin); a multiplexed control
channel reintroduces the demux trade alksocks resolved *for SOCKS5*
(whose RFC structure genuinely has one command per connection — a
file protocol has no such constraint). This trade has different
physics here than it had there; no inherited answer.
- **Framing on the data path.** If sessions are multiplexed, requests
need IDs and payloads need framing (length-prefix + request-id — the
one-way door). If per-session, the payload may be near-pass-through.
Chunk sizes, backpressure (bounded buffers are inherited), and
flow-control for pipelined writes all live here.
- **Read-path streaming.** Range reads (offset+len), EOF semantics,
verified-streaming vs trust-the-store; zero-copy ambitions explicitly
out (channels are the substrate, not splice(2)).
- **Establishment.** `register_openable_with_establisher` with a scope
gate (OQ-FS-10); refused opens are typed errors with per-verb error
mapping (SFTP status codes at the door, faithful engine errors in
protocol — convention 2).
#### OQ-FS-09: ALPN, params, and the resource model
Provisional `alk/fs` (final naming per alkcall ADR-004/006 convention).
Open sub-questions (mostly Phase 1 ADRs with Phase 0 ground):
- The params-object shape (ADR-039 precedent: ALPN-specific, interpreted
by the open handler) — what identifies a produced filesystem resource?
(bucket? branch? scope string? limits?)
- In-process and loopback consumers: the engine half must be directly
embeddable (no channels) — the protocol layer is one substrate among
several, generic over `T` (principle 6). The in-process shape is also
the test seam, the alksocks POC pattern.
- Hub relay story: a filesystem resource traverses relays
terminate-and-re-produce like any produced resource; nothing
alkfs-specific expected — verify during POC rather than assume.
#### OQ-FS-10: Doors — alksftp, mounts, sync (out of crate, but shapes the API)
Family rule: doors are separate crates/features. Phase 0's job is to
keep the protocol layer door-ready: the verb set (OQ-FS-08) must map
onto russh-sftp's `Handler` without translation loss; a FUSE/mount door
needs POSIX-ish error mapping and inode-ish handle semantics — do not
design for FUSE in v1, but record what it would demand so the protocol
doesn't preclude it. Directory-sync tooling (alkfs ↔ host dir) is the
explicit, feature-gated host-FS bridge if/when wanted — never ambient.
#### OQ-FS-11: Multi-tenancy, identity, and path-scope policy
Buckets are the isolation unit (POC-proven free). Open questions: the
`ALKFS_OPEN_SCOPE`-shaped scope gate (alktty/alksocks precedent) — does
scope name buckets, path prefixes, or both; identity → bucket mapping
at the assembly layer (vault references, never plaintext secrets —
family rule); per-path/per-bucket policy as a producer-side policy
callback vs declarative ACL (alksocks OQ-SK-05's dialer-refuses
resolution is the evidence-backed template); write-vs-read grant
separation (a read-only bucket mount is the obvious first split).
### Consumer fit
#### OQ-FS-12: alkgit fit — the question that un-pauses alkgit
The first consumer and the reason this crate moved ahead of alkgit. The
decision tree:
- **(a) alkgit keeps gix-odb native; alkfs serves the general FS.**
Lowest integration risk; but then alkfs doesn't solve the problem that
paused alkgit, and the family carries two storage stories.
- **(b) alkgit's backend traits get an alkfs-backed implementation.**
`GitPackGen` streams a pack *out of* alkfs content (pack assembly is
alkgit-side; the blob bytes come from alkfs reads);
`GitPackIngest` streams a pack *into* alkfs (each unpacked object
lands as content; refs land as metadata rows); `GitRefs` becomes
CAS transactions over alkfs refs. gix-odb's on-disk layout disappears;
git semantics ride alkfs's content addressing. This is the "optimize
for git" path and the probable answer — but it makes alkgit's
workload (pack-scale blobs, O(counts) memory, streaming) the
benchmark OQ-FS-13 must meet.
- **(c) gix-odb *on top of* alkfs** (an odb backend where loose
objects/packs are alkfs files) — keeps gix-odb's pack machinery but
treats alkfs as the disk. Probably the worst of both: gix-odb assumes
filesystem semantics alkfs won't natively provide (mmap, file locks).
Sub-questions: does git's object-id namespace (sha1/sha256) coexist with
alkfs's content addresses — **mostly dissolved by the identity reframe
(2026-09-23)**: git oids *are* the blob layer's addresses now; what
remains is whether git objects ride as whole-file identities (the
natural fit — git objects are small) while alkfs path rows may point at
chunk-manifest roots for large content (OQ-FS-04's hybrid), and how
pack files relate (a pack is just a large blob to alkblobs; unpacked
object rows are the useful representation — the ingestion mapping is
the question). CAS ref transaction shape; whether pack ingestion
budget enforcement (alkgit ADR-009) maps onto alkfs write sessions.
#### OQ-FS-13: Performance shape and budgets
No numbers exist yet. What Phase 0 should collect (empirically, via the
POC register) so Phase 1 can pin budgets: path-resolve cost at realistic
branch depths (POC: sub-ms in-memory; chains of 10+?); small-file
throughput (the inline-BLOB sweet spot claim vs reality at 4KB metadata
ops); large-file write throughput through chunked sessions; pack
gen/ingest streaming rates over a channel vs native gix-odb; memory
bounds under the alkcall channel caps. The alkgit ADR-009 limits shape
is the consumer contract to serve.
### Posture and deferred
#### OQ-FS-14: Distributed sync / multi-node — in scope at all?
POC 3 proved automerge-CRDT sync of the path tree (9 tests, LWW
conflicts, the concurrent-root-init constraint) with content fetched
separately. But sync is a product-layer concern that may not belong in
the engine crate: the family pattern is composition (alkcall relay for
transport, automerge as a third-party sync layer, honker queues as the
local outbox). Working posture: **deferred — out of v1 scope**, recorded
so the metadata schema doesn't preclude it (branch/paths rows are
already CRDT-encodable per POC 3's document structure). Revisit when a
consumer needs multi-node.
#### OQ-FS-15: WASM posture
The expected shape (convention 4): wasm-clean protocol/ops layer; native
storage engine behind features (real FS/SQLite never compiles to
wasm32-unknown-unknown). Open: does any consumer actually want the
protocol layer in wasm (a sandboxed alkfs consumer, mirroring alksocks'
sandboxed-SOCKS-consumer story)? If none is in sight, the honest answer
may be alkhttp-style native-only (the "wasm-clean is preferred, not
mandatory" precedent) — but the protocol layer being wasm-clean by
construction costs little if the engine split exists anyway. Decide
deliberately once the split (OQ-FS-01) exists; verify with
`cargo check --target wasm32-unknown-unknown`.
#### OQ-FS-16: The remote-mount workload — the anchor use case
**Added 2026-09-23.** The concrete deployment this crate exists for (the
user's own daily workflow): a dev-server workspace mounted on a local
Linux machine under `remote/` — today via sshfs/vanilla SFTP, with
known slowness:
- **Why vanilla SFTP is slow (the honest decomposition):** a network
round trip per syscall (stat storms during directory walks; sshfs
even translates `SeekFrom::End` into a real `fstat` round trip);
attribute-invalidation chatter; no server-side index (the SFTP door
shells to real FS syscalls, each paying host-FS costs); attr-cache
and cache-timeout knobs that trade staleness for speed. The POC's
mapping table already noted `stat` → single indexed lookup is
*cheaper* than a real FS fstat — an appfile-indexed VFS fixes the
server-side half structurally.
- **What the alkfs shape adds:** server-side indexed metadata (stat/
readdir from index rows, no syscall per attribute); pipelined writes
and batched reads native to the protocol (the russh-sftp client's
`write_nowait` + ack window rides the chunked write session);
optional client-side caching/prefetch as door-local policy (a mount
door can cache content chunks locally — content-addressed means
cache coherency is a branch-head-invalidation problem, not a
byte-consistency problem); directory-listing snapshots.
- **What this anchors:** the verb set (OQ-FS-08) must make the
readdir/stat/read/write path fast, not merely correct; the session
model (OQ-FS-08's per-file-channel vs multiplexed question) matters
most for directory-walk fan-out; OQ-FS-13's budget table should
measure against sshfs-over-localhost baselines.
- **alksftp vs a mount protocol:** vanilla SFTP clients (sshfs) work
against any SFTP door with zero client install — that compatibility
is worth keeping as the floor. A leaner alkfs-native mount protocol
(OQ-FS-08's framing question) is the speed tier; whether it warrants
its own FUSE-facing door early or later is a sequencing question
deliberately parked until the protocol shape exists.
This OQ is the *consumer pull* behind the whole crate — if the mount
use case stops mattering, OQ-FS-08's urgency drops with it.
## Candidate POC register (proposals — none run yet)
POC placement conventions (inherited from alktunnels/alksocks): a POC
needing this repo's code runs in a worktree (`.worktrees/research/
<task-id>/` per `docs/sdd_process.md`); a self-contained POC runs as a
standalone crate in the global workspace with findings written into
`docs/research/` here. Findings always land in `docs/research/`.
| # | Candidate | Hypothesis to validate | Feeds |
|---|---|---|---|
| 1 | Engine skeleton: path tree + inline-blob store in one SQLite file | One-file metadata+blob-metadata (OQ-FS-02) holds up under the POC's 15-test suite ported over; single-transaction durability ordering (principle 3) is expressible | OQ-FS-02, OQ-FS-05 |
| 2 | Write path at pack scale | Chunked write sessions handle 100MB+ streams (spill-to-file regime) with correct durability ordering and crash-abort semantics | OQ-FS-05, OQ-FS-13 |
| 3 | alkblobs store skeleton (sha1/sha256 identity) | Own-store direction (OQ-FS-03): algorithm-tagged hash ids, bucket isolation, inline-vs-spill layout, manifest-of-chunks for large content — measure against the probe's store-seam map as the API checklist | OQ-FS-03, OQ-FS-04 |
| 4 | Chunking probe (paper + micro-bench) | Whole-file vs chunk manifest: dedup gains on realistic workloads (agent workspaces, datasets, logs — git objects are single-chunk and ride whole-file), write-path impact, metadata size — enough to decide the format one-way door or at least stage it | OQ-FS-04 |
| 5 | Serving protocol spike | SFTP-verb protocol over channels: per-file-channel vs multiplexed-handles session model, open cost at directory-walk and git-clone fan-out, framing sketch (not a wire ADR) | OQ-FS-08, OQ-FS-09 |
| 6 | alkgit seam spike | `GitPackGen`/`GitPackIngest` shaped against an alkblobs-backed engine — does the trait family survive contact; git oids as native addresses | OQ-FS-12 |
| 7 | GC walk prototype | Reachability-mark GC over path rows + refs: correctness on shared content across branches, cost model, orphaned-session reaping | OQ-FS-07 |
| 8 | Mount-workload baseline | Measure the *current* pain (sshfs against this dev server): op latencies for stat/readdir/read/write at realistic workspace scale — the baseline OQ-FS-16 improvements must beat | OQ-FS-16, OQ-FS-13 |
Order is indicative, not committed: #1/#3 de-risk the engine cores
(alkfs metadata, alkblobs store); #2 stress-tests the write path; #4
resolves the storage-format one-way door; #5/#6 are only worth running
once #1/#3 exist (the protocol needs an engine to serve); #8 can run
anytime (it measures today's sshfs, no alkfs code needed) and its
baseline ages well if taken early. The register exists so POC work has
home ids; expect entries to be split, merged, or dropped as OQs sharpen.
## Survey / prior-art list
Read/verified (cited above):
- alknet-filesystem POCs —
`/workspace/@alkdev/alknet/docs/research/alknet-filesystem/
poc-summary.md`, `alknet-blobs-external-store-probe.md`; POC crates
`/workspace/alknet-filesystem-poc`, `/workspace/alknet-fs-sync-poc`.
- iroh-blobs — `/workspace/iroh-blobs` (v0.100 checkout + `DESIGN.md`),
published 0.103 in cargo cache (probe source); the family's reference
notes at `/workspace/@alkdev/alknet/docs/research/references/iroh/
iroh-blobs/` (overview, key types, transfer protocol, storage).
- alkgit — `docs/architecture/backend.md` (the trait seam),
`docs/research/{vision,gitoxide,git-protocol,poc2-findings}.md`.
- russh-sftp — `/workspace/russh-sftp` (`server/handler.rs`, client
`File` pipelining).
- SQLite appfileformat — https://sqlite.org/appfileformat.html; honker
— `/workspace/honker` (`honker-core` 0.2.4).
- rudolfs (git-lfs server) —
`/workspace/@alkdev/alknet/docs/research/references/gitlfs/
rudolfs-reference.md`.
- alkcall — ADRs 034/035/036/037/039/042/049/050/051, ledger
CF-005/CF-006 (`docs/architecture/decisions/`); alktty/alktunnels/
alksocks Phase 0 docs as the format/template precedents.
To evaluate (research-specialist queue, feeding the OQs named):
- **Chunking/CDC prior art** — FastCDC, rollsum (bup), rsync-style
boundaries; git tree-object hashing as the manifest shape (the
versioned-hash constraint's precedent, OQ-FS-03/04); `bao_tree`'s
chunker as a chunk-boundary *algorithm* candidate (decoupled from its
BLAKE3 identity).
- **CAS filesystems** — Plan 9 fossil+venti (the branch/CAS ancestry the
POC name-checks), IPFS unixfs/iros DAG shapes, OST/lessfs-style
inline-vs-outboard hybrids (OQ-FS-04, OQ-FS-06).
- **git storage internals** — gix-odb pack/idx layout and
`gix-pack::data::input` streaming (alkgit research has the survey;
re-read for OQ-FS-12's object/pack mapping questions).
- **SQLite durability posture** — WAL + `synchronous=NORMAL` vs `FULL`
under the commit-boundary contract; SQLite-as-blob-store limits
(OQ-FS-02, OQ-FS-05).
- **FUSE/mount doors** — rust bindings shape, POSIX semantics alkfs
would need to fake; sshfs latency characteristics (the OQ-FS-16
baseline's "why it's slow" decomposition, verified against real
measurements from POC #8 rather than folklore) (OQ-FS-10,
OQ-FS-16; defer design, record constraints).
## Convergence checklist (what Phase 0 must produce)
- [ ] Vision + guiding principles captured (this doc, §Vision — the
scope sketch needs confirmation or cutting, OQ-FS-01)
- [ ] Prior-art pass complete: alknet-filesystem POCs re-read and
mapped (done); iroh-blobs surface re-verified against the probe's
0.103 findings; git/git-lfs shape pinned against alkgit's backend
contract; CDC/chunking survey (OQ-FS-04 input); CAS-filesystem
survey
- [ ] OQ-FS-01 (crate scope) — converged: the alkblobs/alkfs split
confirmed or cut; repo shape (workspace vs two repos); trait seam
at the blob boundary or concrete dependency; explicit non-goals
- [ ] OQ-FS-02 (metadata store) — converged: substrate, one-file-vs-two
(blob metadata placement, now scoped to alkblobs), durability knob
- [ ] OQ-FS-03 (blob substrate) — converged: the own-store direction
pinned down (what transfers as findings, what's built fresh)
(POC #3)
- [ ] OQ-FS-04 (hashing/chunking) — converged: identity settled in
direction (sha1/sha256); dedup granularity + manifest format are
the one-way door (POC #4)
- [ ] OQ-FS-05 (write path) — durability/concurrency/spill semantics
pinned (POC #2)
- [ ] OQ-FS-06 (branch/snapshot/refs) — model chosen; ref namespace
decided; merge explicitly in-or-out of v1
- [ ] OQ-FS-07 (GC) — liveness shape chosen; retention/reaping policy
pinned (POC #7)
- [ ] OQ-FS-08 (file protocol) — verbs, session model, framing sketch;
the wire ADR itself is Phase 1's (one-way door, first consumer in
sight) (POC #5)
- [ ] OQ-FS-09 (ALPN/params) — naming + params ground collected; ADR
in Phase 1
- [ ] OQ-FS-10 (doors) — protocol kept door-ready; nothing designed in
- [ ] OQ-FS-11 (tenancy/policy) — scope-string + policy shape settled;
bucket semantics across the alkblobs/alkfs seam
- [ ] OQ-FS-12 (alkgit fit) — the (a)/(b)/(c) decision made; alkgit
un-pause plan written (POC #6)
- [ ] OQ-FS-13 (budgets) — numbers collected (incl. the sshfs baseline,
POC #8); budget table drafted
- [ ] OQ-FS-14 (sync) — explicitly deferred or scoped with evidence
- [ ] OQ-FS-15 (wasm) — decided once the engine/protocol split exists
- [ ] OQ-FS-16 (remote mount) — the anchor workload's requirements
written down (verb-set implications, caching shape); sequencing
(SFTP floor first vs native protocol tier) decided
- [ ] Targeted POCs run + findings in `docs/research/`
- [ ] Converge: recommended approach written up, ready to hand to the
Architect for Phase 1