Every write to storage produces a crossing in the central SQLite index. The crossing always lives at its to_addr. The payload is either inline (small data) or an IOU marker pointing at where the content actually lives.
SQLite (always)
to_addr: :pki:keys:my-key
from_addr: :system:pki
type_addr: :types:pki:key
sig: "abc123..."
at: "2026-04-10T12:00:00Z"
payload: { _iou: "uuid", _driver: "file_store", _args: { path: "/keys/my-key.pem" }, _size: 8192 }
The SQLite index is the source of truth for what happened, when, and signed by whom. Content storage is just where the bytes live. You can swap content backends without losing provenance. You can query across all writes without hitting every backend.
{
_iou: "uuid-here", # unique handle
_driver: "file_store", # which plugin
_args: { ... }, # driver-specific args
_size: 8192 # bytes (optional, for indexing/quotas)
}
The _args hash is opaque to the index. Each driver defines its own arg schema:
| Driver | Args |
|---|---|
file_store |
{ path: "/abs/path" } |
yaml_store |
{ path: "/identities.yml", at: "alice.private_key" } |
db_store |
{ connect: "postgres://...", table: "blobs", id: "uuid" } |
http_store |
{ url: "https://...", method: "GET" } |
ssh_store |
{ host: "server", path: "/data/file" } |
s3_store |
{ bucket: "...", key: "...", region: "..." } |
A driver is a plugin that implements two methods:
module Wanderland
module Storage
module Drivers
class FileStore
# Write content, return an IOU marker.
# The driver picks the args; caller doesn't dictate the path.
def write(content, hint: nil)
path = generate_path(hint)
File.write(path, content)
{
"_iou" => SecureRandom.uuid,
"_driver" => "file_store",
"_args" => { "path" => path },
"_size" => content.bytesize
}
end
# Read content given the args from a marker.
def read(args)
File.read(args["path"])
end
end
end
end
end
Drivers register at process load time, same as boundaries:
Wanderland::Storage::Drivers.register("file_store", FileStore.new(root: "/data/files"))
Wanderland::Storage::Drivers.register("yaml_store", YamlStore.new)
Wanderland::Storage::Drivers.register("http_store", HttpStore.new)
Storage::Registry.append(record)
→ SQLite driver receives the record
→ Check payload size against threshold
→ If small: store inline
→ If large: extract via configured driver
→ driver.write(content, hint: to_addr) → marker
→ replace payload leaf with marker
→ INSERT lean record into SQLite
Multiple IOU extractions per record are supported — IOU.extract walks the payload tree and replaces every large leaf with a marker. Each marker can use a different driver based on configuration (size, type, content pattern).
Storage::Registry.at(addr)
→ SQLite returns lean record(s)
→ Caller receives records with IOU markers in payload
→ Optional: hydrate the record
→ Walk payload, find IOU markers
→ For each marker: lookup driver by name, call read(args)
→ Replace marker with content
Hydration is opt-in. Most queries don't need the full content — they just need to know what exists. Hydrate when you need the bytes.
The current FileDriver and YamlStore get refactored into IOU driver plugins:
FileDriver → Drivers::FileStore (write blobs to filesystem, return path-based marker)IDP::YamlStore → Drivers::YamlStore (read keyed values from YAML files)The SQLiteDriver becomes the universal index. Other backends become content stores accessed via IOU markers.
The storage mount spec declares which drivers extract for which prefixes:
storage:
mounts:
":pki:":
driver: sqlite
path: ":memory:"
content_drivers:
- condition: { size: { gt: 4096 } }
driver: file_store
args: { root: "/var/wanderland/keys" }
":streams:":
driver: sqlite
path: "streams.db"
content_drivers:
- condition: { size: { gt: 1024 } }
driver: file_store
args: { root: "./content/streams" }
The SQLite driver consults the content_drivers list when appending. First matching condition wins. No match = inline.
Because the crossing record (with sig and trace) always lives in SQLite, you can:
file_store to s3_store by reading and rewriting markersThe content is the leaf. The crossing is the root. The marker is the bridge.
Every driver write is part of a crossing, which is signed by the writing identity. The marker preserves the link — you can follow the marker, read the content, and verify the original crossing's signature still matches the (canonical) record. Tampering with content in the backend doesn't break the index; it breaks the round-trip verification.
For stricter integrity: the marker can include a content hash (_sha256) so a rehydrate-and-verify round trip catches backend tampering immediately. The index sig covers the marker (which includes the hash). The hash covers the content. Two-layer integrity.
Drivers registry — Storage::Drivers.register(name, driver), lookup(name)IOU to produce typed markers (_driver, _args)SQLiteDriver to consult content_drivers config on appendSQLiteDriver#hydrate to follow typed markers via the Drivers registryFileDriver to Drivers::FileStoreIDP::YamlStore to Drivers::YamlStore (accept reads keyed by yaml path)content_drivers config