Skip to content

Storage: make tinystoragedrivers the only storage owner, provisioned to every tiny* crate through ports and TinyBus #7522

Closed

Description

@senamakel

Summary

Today storage is split across three models:

  1. tinystoragedrivers ports. DocumentStore, StreamStore, BlobStore, SecretStore and StorageBackend::for_scope, selected by OPENHUMAN_STORAGE_URL and storage-* features in openhuman-core.
  2. Per-crate native backends. tinyflows-sqlite, raw rusqlite in tinymcp, tinyagents-session, -graph and -harness, and a direct mongodb dependency in tinyflows-adaptive.
  3. Hand-written JSON and JSONL files in almost every other submodule.

Modules loaded as cdylibs over TinyBus cannot reach the host's storage at all. They keep their own files, or call narrow persistence callbacks the host serves for one module (tinychannels SavePendingDelivery, etc.).

Goal: tinystoragedrivers is the single owner of storage implementations. openhuman-core picks the backends (storage-sqlite, storage-mongodb, storage-file, storage-s3) through feature flags. Every child tiny* crate depends only on the storage ports and receives a scoped handle. Modules running out of the host's address space get the same ports, served over a new storage bus contract. No child crate ships a storage-specific crate or dependency. For example, tinyflows-sqlite goes away.

Current state (inventory)

Native storage in child crates:

Submodule / crate Native storage today Driver path exists?
tinyflows / tinyflows-sqlite flows.db, jobs.db, checkpoints.db, raw SQL; drafts as JSON files partial: tinyflows-drivers (FlowCatalogDocuments, CronDocuments, DriverStateStore, DriverCheckpointer). Drafts have none.
tinyflows / tinyflows FileWorkflowStore, FileStateStore, FileCheckpointer (JSONL), notes journal via tinyflows-drivers
tinyflows / tinyflows-adaptive Ledger/Vault on SQLite and a direct mongodb 3.6 dependency yes (drivers/ledger, drivers/vault)
tinyagents / -session sessions.db (raw SQL + FTS5), SqliteRunLedger, tool_effects, JSONL transcripts, turn-state JSON, JSONL threads + in-memory inverted index partial: DriverSessionStores; DriverRunLedger returns Unsupported for team-task claim/complete/shutdown, prune_*, trim_session_messages, apply_retention, reindex_fts
tinyagents / -graph SqliteCheckpointer, FileCheckpointer, SqliteTaskCache checkpointer yes; TaskCache no
tinyagents / -harness FileStore, JsonlAppendStore, SqliteResponseCache, EmbeddingCache (JSON file of Vec<f32>), FileStatusStore, artifacts/attachment stash/media on disk Store/AppendStore yes; the rest no
tinyagents / -tasks JsonlTaskStore, JsonlCompletionStore no
tinymcp raw rusqlite: mcp_clients.db (servers, env secrets, registry index, tool cache), mcp_audit.db no
tinychannels remote-session JSON map; WhatsApp state in memory only no (host callbacks)
tinyskills FileCatalogStore, SKILL.md trees no
tinyconnectors FilePrefsStore, daily JSONL trigger archive no
tinywallet / x402 payments.jsonl + in-memory budget reservation no
tinybox FileStore, jail index.json no
tinyjuice CcrStore disk tier no
tinyinference (nested) OAuth PKCE pending JSON, media blobs no

Legitimate exceptions. These read external SQLite databases read-only and are not storage:

  • tinychannels iMessage chat.db
  • openhuman-app imessage scanner
  • tinymemory legacy-import
  • tinymemory engines themselves, which are remote CortexDB over HTTP

Host side:

  • crates/openhuman-core/src/storage/ holds the install slot, scopes and block_on.
  • Without a URL ("classic" mode), the per-domain SQLite files stay in use.
  • rusqlite is still a direct dependency of openhuman-core and openhuman-cli.
  • Root Cargo.toml [patch] exists only to keep one DocumentStore trait and one libsqlite3-sys across tinyflows' git-tag pins.

TinyBus:

  • No storage contract exists.
  • Calls are async JSON with a 16 MiB frame cap.
  • Bulk data uses ai.tinyhumans.tinybus.Stream: 512 KiB chunks, windowed backpressure.
  • Signals are available for push notifications.
  • CallContext::authenticated_sender() exists, but there is no policy layer yet (ROADMAP M4).

Design

1. One port surface, two transports

  • Ports stay in tinystoragedrivers-core. These are DocumentStore, StreamStore, BlobStore, SecretStore, ScopedStorage and Capabilities, plus gap fixes from item 5. Child crates depend on tinystoragedrivers-core only: no driver crate and no features that pull a database client.
  • In-process fast path. The host hands a child an Arc<dyn …> / ScopedStorage directly, with zero serialization. This covers tinyagents, tinyflows and other linked libraries.
  • Bus path, for cdylib or out-of-process modules. A new tinystoragedrivers-bus contract crate (sync, no I/O, serde only, like every other *-bus crate) defines ai.tinyhumans.tinystorage.Storage. It has one object per port: Documents, Streams, Blobs, Secrets.
  • Bus client. A tinystoragedrivers-client crate (or a bus-client feature of core) implements the same port traits over a TinyBus proxy. Module code is identical whether storage is local or remote.
  • Host server. openhuman-core serves the interface from the installed StorageBackend (modules/storage_host.rs, modelled on tokenjuice_host.rs / wallet_host.rs). This replaces narrow per-module persistence callbacks such as the tinychannels Save/LoadPendingDelivery.
  • Rename (open question). Rename tinystoragedrivers → tinystorage with an adapter/ports crate. That is optional and cosmetic; decide before child crates re-pin.

2. Backends compiled in one place

  • openhuman-core features storage-sqlite | storage-mongodb | storage-file | storage-s3 are the only switches. They are forwarded down the chain (embed → tinyhumans → rpc → cli/tui/app) and checked by check-feature-forwarding.mjs.
  • Child crates expose no sqlite/mongo/file features.
  • Retire "classic" per-domain .db mode. The default desktop URL becomes sqlite:<workspace>/storage, and existing per-domain DBs and JSON/JSONL files get a one-time import. Import code lives host-side (storage::local / an import module), reading legacy files read-only, never modifying them.

3. Security

  • Scope is never taken from the call body. For bus calls, the host derives scope from the broker-authenticated caller (CallContext::authenticated_sender() → module id → Scope) combined with the current profile/agent (storage::scope_from). Implement with call_with_context.
  • Per-module grant. A module's manifest or modules.toml declares the collections, stream and blob prefixes, and capabilities it may use. The host enforces this on every call. Unknown or undeclared collections are refused.
  • Secrets. The Secrets object is #[tinybus(confidential)] (attested in-process modules only). Values stay Zeroizing and are encrypted per scope with DerivedKeys/KMS. tinymcp mcp_client_env and OAuth bundles move to it.
  • Schema rights. Restricted existing_schema() handles for modules: only the host runs ensure_collection or migrations, from the module's declared CollectionSpecs.
  • Fencing. Write fencing (Fence/lease epoch) is preserved across the bus: the fence is applied by the host-side handle, not trusted from the module.
  • Audit. Every bus storage call is logged with module id, scope, op and collection (no payloads).

4. Speed, streaming and scale

  • Linked libraries use the in-process path with no copy.
  • Bus documents: batch ops (atomic_batch, query with cursor pages, append_batch) to amortize round trips. Page size is capped below the 16 MiB frame.
  • Blobs over tinybus.Stream (put_stream/get_stream/get_range) with windowed backpressure. Presigned URLs are used when the backend supports them (S3), so large bytes skip the bus.
  • Watch is projected to bus signals (payload-free invalidations, as the port already is).
  • Scale: the MongoDB backend plus fencing covers multi-process/SaaS. Cursor paging should become keyset rather than offset in the SQLite and Mongo drivers (tracked upstream).

5. Port gaps to close upstream in tinystoragedrivers first

  • Vector capability (Capability::Vector: upsert embedding, top-k cosine). It serves harness EmbeddingCache, the NamespacedStore semantic query, and tinychannels Memory::recall. Its own spec decides whether it belongs in the ports or stays with tinymemory.
  • FullText parity. session sessions_fts, the threads n-gram index and the tinymcp index search map to DocumentStore::search. Define reindex and ranking semantics.
  • Atomic claim/CAS coverage for DriverRunLedger team-task claim/complete/shutdown, retention/prune, and trim_session_messages.
  • TTL used consistently for caches: TaskCache, ResponseCache, CcrStore, AttachmentStash, catalog age.
  • Blob "tree" view for tinyskills bundles, which must materialize as real directories for cap-std discovery. The store holds the blobs; the host materializes them to a cache dir.

Plan (child issues; module/upstream PR first, then gitlink + host adapter)

Acceptance criteria

  • cargo tree -i rusqlite / -i mongodb / -i object_store show only tinystoragedrivers crates plus the allowlisted read-only readers.
  • No tiny* crate has storage-backend features. Backends are selected solely by openhuman-core storage-* features and OPENHUMAN_STORAGE_URL.
  • The tinystoragedrivers conformance suite passes against memory, sqlite, file, mongodb and the bus client.
  • A cdylib module persists, streams a >16 MiB blob, and receives a watch signal through the bus. It cannot read another module's collections or another scope (negative tests).
  • Desktop upgrade imports existing per-domain DBs and JSON files losslessly (fixture-based test), and legacy files stay untouched.
  • Headless e2e (scripts/e2e-core.sh) is green on sqlite and mongodb URLs.

Non-goals

  • Changing tinymemory's remote CortexDB engine.
  • Replication.
  • A query language beyond the existing Filter/Query.

Activity

  1. senamakel commented on Oct 12, 2026

    @senamakel
    MemberAuthor

    Stage #7635 progress: tinystoragedrivers PRs #33/#34/#35 merged, v0.6.0 tagged. Bus contract crate, bus client implementing all port traits with loopback conformance, blob tree view, run-ledger/FTS/TTL conformance and s3 feature are in. Still open on #7635: mongodb conformance run (CI-only), native blob TTL, real TinyBus proxy transport + module-side server registration. #7635 left open and unticked here; #7636 can start against the contract but needs the proxy transport for end-to-end.

  2. senamakel commented on Oct 12, 2026

    @senamakel
    MemberAuthor

    Stage #7636 (host) progress: PR #7646 merged (partial). tsd v0.6.0 pinned via tinyagents#420; storage_host + per-module grants/audit/fencing serve the Storage contract with scope from the authenticated caller; storage-s3 forwarded; 35 tests. Still open on #7636: default sqlite URL + legacy import, classic-mode removal, rusqlite removal, module-side transport/streams, first real module grant. #7636 stays open and unticked here.

  3. senamakel commented on Oct 12, 2026

    @senamakel
    MemberAuthor

    Progress on #7636 (openhuman host): #7646 merged (storage_host, per-module grants, audit, storage-s3) and #7648 merged (read-only legacy import of approvals/devices/notifications/task_sources; direct rusqlite removed from core/cli). Still open: default sqlite URL, session/cron/flow importers (need upstream PRs first), and classic-mode retirement. #7636 stays open and unticked here.

  4. senamakel commented on Oct 12, 2026

    @senamakel
    MemberAuthor

    Stage #7644 done: tinystoragedrivers#36 merged and released as v0.7.0. Adds the tinystoragedrivers-busproxy crate (real TinyBus StorageTransport, module connect_storage, host serve + StorageEndpoint/PerCallerEndpoint) and native blob TTL (memory/sqlite/file/s3, bus contract v3). Leftovers (mongodb TTL, expired-blob sweeper, two tinybus gaps, review nits) tracked in #7655. Next: re-pin and host switch in #7636.

  5. senamakel commented on Oct 13, 2026

    @senamakel
    MemberAuthor

    Stage #7637 (tinyflows ports-only) done: tinyflows#128 deleted tinyflows-sqlite and the File* stores and added DriverWorkflowStore plus tinyflows-import; released v0.8.14. openhuman#7661 re-pinned (tinyagents#421 moved tsd to v0.7.0), moved cron/flows/checkpoints onto the ports and imports legacy jobs.db/flows.db/checkpoints.db/drafts. #7637 closed. Leftovers (crate fold, root tsd [patch] removal) tracked in #7662, added to the Plan.

  6. senamakel commented on Oct 13, 2026

    @senamakel
    MemberAuthor

    tinyhumansai/tinyjuice#78: tinyjuice 0.9.1 release carrying the nested tinyagents v2.2.0 pin; consumed by openhuman#7686 (#7686)

  7. senamakel commented on Oct 13, 2026

    @senamakel
    MemberAuthor

    Stage #7638 done: tinyagents#422 (driver impls, SQLite/File/JSONL stores deleted, importer), tinyjuice#77/#78 (v0.9.1), openhuman#7686 (re-pin tinyagents v2.2.0, host on driver stores, default sqlite:/storage, sessions importer, classic mode removed). Leftovers (file fallbacks without a backend, upstream JSONL transcript/threads/media, storage::rusqlite re-export, coverage, two pre-existing test failures) tracked in #7688.

  8. added
    priority: p3Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect.
    on Oct 13, 2026
  9. tinysweeper commented on Oct 13, 2026

    @tinysweeper

    Tracking/design issue for making tinystoragedrivers the single storage owner with a TinyBus storage contract serving all tiny* crates via ports. It is an architectural epic with a child-issue plan, not a report of a broken behavior or a discrete request; most children are already merged and the remainder is tracked in dedicated issues (#7636, #7655, #7662, #7688, #7639-#7642). Remaining work is long-horizon and no user-facing breakage is described, so priority is p3.

    Labelled priority: p3.

  10. added theissue type on Oct 13, 2026
  11. senamakel commented on Oct 13, 2026

    @senamakel
    MemberAuthor

    Stage #7639 (tinymcp): partial. tinymcp#85 merged (v0.16.0): ports-based storage, rusqlite gone, Secrets for env/OAuth. openhuman#7693 merged: storage grant declared. Open: attested recipient for host Secrets, tinymcp#89 merge+release then re-pin, legacy .db import, and a call on main's current 0.16.1 pin (state likely not persisted). #7639 stays open; line unticked.

  12. senamakel commented on Oct 13, 2026

    @senamakel
    MemberAuthor

    Stage 6/8 (#7640, tinychannels) done: #7698 merged, re-pinning tinychannels v0.1.30 (storage contract v21). Persistence host callbacks and the file ChannelDeliveryStore are removed; the module owns delivery, remote-session and WhatsApp collections in its storage scope (WhatsApp durability in tinychannels#87). Legacy channel-delivery.json is imported once, read-only. Caveat: tests needing the linked-module fixture stay blocked on the tinybus epic (one #[ignore]d, two already failing on main).

  13. senamakel commented on Oct 13, 2026

    @senamakel
    MemberAuthor

    #7642 gate: PR #7700 (ready). Verified on branch: cargo tree -i rusqlite shows only tinymemory-integrations (legacy-import), openhuman-app (imessage) and tinystoragedrivers-sqlite; mongodb and object_store are absent from both graphs. The 'cargo tree' acceptance bullet is therefore met once #7700 merges; the other acceptance bullets (bus conformance, cdylib e2e, legacy import, headless e2e) remain open, and the plan item for #7642 should be ticked on merge. Not closing #7522.

  14. senamakel commented on Oct 13, 2026

    @senamakel
    MemberAuthor

    Stage update: #7642 (CI gate) done and closed via #7700 (merged). scripts/ci/check-storage-owner.mjs runs in pnpm rust:layout (alias storage:owner), self-test in scripts/tests/check-storage-owner.test.mjs. Allowlist: tinychannels, openhuman-app, tinymemory-integrations. Plan checkbox for #7642 ticked. #7522 stays open: bus conformance, cdylib e2e, legacy import and headless e2e acceptance items remain.

  15. 15 remaining items

  16. senamakel commented on Oct 14, 2026

    @senamakel
    MemberAuthor

    Stage #7772 done via #7785: probe-cdylib negative e2e (10 tests across real ABI), mongodb job green, build break in modules::expiring_blob_module_scopes fixed, diff coverage 87.9%. Stream/watch positives deferred to #7786 (needs #7781 tinybus 0.1.5). Known unrelated CI failures: EX63 runner lacks sudo, docs link check 404s.

  17. senamakel commented on Oct 14, 2026

    @senamakel
    MemberAuthor

    Stage #7781 closed. tinybus 0.1.5 move (#7788), tinyagents to main (#7782), module tsd v0.10.0 re-pins (#7788), profile-scope blob sweep (#7795) and SQLite clock injection (tinystoragedrivers#41, released v0.11.0) are all merged.
    Remaining: moving vendor/tinyagents to 37ed7a40 (tsd v0.11.0) needs the ArtifactOffload/with_render_root host adaptation, tracked in #7783.
    #7786 (cdylib stream/watch e2e) is now unblocked by the tinybus 0.1.5 move.

  18. senamakel commented on Oct 14, 2026

    @senamakel
    MemberAuthor

    Stage #7786 done: #7800 merged. The host now serves Blobs.PutStream/GetStream and Documents.WatchOpen through tinystoragedrivers-busproxy.
    The probe-cdylib e2e covers a 20 MiB blob round trip, a watch signal reaching the subscriber, and cross-module isolation. The oversize-frame hang is fixed in tinybus#69.
    Remaining (tinybus#70 re-pin, CI Fast on EX63) is tracked in #7803.

  19. senamakel commented on Oct 14, 2026

    @senamakel
    MemberAuthor

    Stage #7754 progress: still open. Done: tinyflows crate-split decision (tinyflows#135, #137, openhuman#7752). Blocked: removing the root/app tinystoragedrivers [patch]. A re-check on upstream/main 7fe9f80 (includes #7788) shows tinyagents takes tsd by path, the modules by git tag v0.10.0 (tinybox/tinyjuice with a version range on top), and tinyflows by git tag v0.7.0. Cargo treats git and path as different packages, so removing the patch would link two tinystoragedrivers-core and two libsqlite3-sys. Remaining: either publish the tsd crates to crates.io for all consumers, or move tinyagents onto the common git tag and bump tinyflows v0.7.0 to v0.10.0. This is a manifest-level conclusion; I did not run cargo tree. No PR opened. The #7522 Plan line stays unticked.

  20. senamakel commented on Oct 14, 2026

    @senamakel
    MemberAuthor

    #7783 stage done via #7807 (merged): cross-process claim for legacy importers, triage of baseline failures, clippy fixes, diff coverage measured (#7686 75.1%, #7766 92.6%, #7782 88.2%).
    Remaining work moved to #7809: JSONL transcript and tool-result stores (tinyagents#453), one full-suite-only channels test failure, #7686 coverage below 80%, a rust:layout line-limit failure on main.
    #7783 closed and ticked.

  21. senamakel commented on Oct 14, 2026

    @senamakel
    MemberAuthor

    Closing. Every acceptance criterion is met and verified on main (fd9ef4f):

    Open follow-ups, not acceptance criteria:

  22. senamakel commented on Oct 14, 2026

    @senamakel
    MemberAuthor

    #7803 stage: openhuman#7813 merged, moving vendor/tinybus to f11987b (tinybus#70). Local gates green (cargo check, storage_host tests 44 passed, layout checks). EX63 lane was still running at merge, so confirmation and the optional pre-fix test check are tracked in #7814 (added to the Plan after #7803). #7803 stays closed.

  23. senamakel commented on Oct 14, 2026

    @senamakel
    MemberAuthor

    Stage #7809 progress: #7821 merged (channel test fixed at root, #7686 coverage 83.4%, layout gate already green). #7809 stays open: transcripts/ToolResultArtifactStore onto driver ports is blocked on tinyagents#453 (open), then a vendor/tinyagents re-pin. Side note: composio test direct_connected_integrations_fetches_schemas_without_backend_composio_route fails in the full lib run on main.

  24. senamakel commented on Oct 14, 2026

    @senamakel
    MemberAuthor

    Stage #7754 done: PR #7833 merged. All modules and the host take tinystoragedrivers from one git tag (v0.11.0); the root and app [patch] tables are gone, one DocumentStore and one libsqlite3-sys in both graphs. Re-pinned tinyflows, tinybox, tinychannels, tinyjuice, tinymcp, tinywallet, tinyconnectors, tinyskills. Leftovers not in the Cargo graph tracked in #7834 (added to the Plan).

  25. senamakel commented on Oct 15, 2026

    @senamakel
    MemberAuthor

    Stage #7809 progress: tinyagents#461 merged and released as v2.7.0 (WorkspaceStores + one-time read-only JSONL transcript import on driver ports). vendor/tinyagents re-pin and host switch (session_store, threads edit) are done locally on storage-7809-host-1 but unpushed; remaining hosts need tinyagents port variants (background append, view, spend). Then path-API removal in tinyagents, re-pin, close tinyagents#453, plus ToolResultArtifactStore. #7809 stays open and unticked here. #7834 not worked in this stage.

  26. senamakel commented on Oct 15, 2026

    @senamakel
    MemberAuthor

    Stage #7834 done: tinymemes#14 (v0.4.0), tinyjuice#89/#90 (v0.10.5) and openhuman#7854 merged. tinymemes is on tsd v0.11.0 with unified tinybus; nested tinyjuice tinyagents is re-pinned; one tinystoragedrivers-core v0.11.0 in root and app graphs. tinyflows crate split stays as a recorded decision (tsd-core unpublished). #7834 closed; leftovers (tinymemes FileIndexStore, tinybus [replace]) are noted on it.

  27. senamakel commented on Oct 15, 2026

    @senamakel
    MemberAuthor

    Stage #7809 close-out: tinyagents#466 (v2.9.0, path-based transcript API removed, #463 follow-ups fixed) and openhuman#7871 (re-pin, reader keying, session_store race fixes) merged; tinyagents#453 closed. #7809 is closed and ticked. The ToolResultArtifactStore half moved to #7872 (added after #7809 in the Plan, unchecked). Open caveats: the 80% coverage gate was not run locally, and the Release workflow still fails with GH013 at the push step (v2.9.0 created by hand).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementpriority: p2Soon. Real but survivable — a rough edge, a gap, a thing that will bite later.

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions