Content-addressable storage, as a thin layer above apotheca.
One cella, hash → bytes, with collision treated as benign dedup. Nothing else.
Specification at v1.0 RC1. The Rust reference implementation is published on crates.io as syntheca v0.3.1 (binary syn, library syntheca). v0.3 tracks apotheca v0.3 (nomenclature: cella / depositum / deposit; deposit_cas fast-path) and surfaces the underlying cella's pinax (compare-and-swap) namespace as a transparent pass-through, so callers who hold a syntheca::Cella for content-addressed bytes can store mutable pointers (state-chain heads, history pointers) on the same cella without juggling a separate apotheca::Cella.
Or jump to the changelog.
syntheca — Greek syn- (together, with) + theca (case, repository, from θήκη). The same syn- as in synchronize (together-in-time) and synthesis (together-placing); the prefix carries the sense the design pivoted on — bytes brought together under a hash that synthesizes them into one name. The compound is properly Greek (no mixed roots) and reads naturally in the -theca register established by apotheca below it.
A syntheca cella is an apotheca cella with the contract that the name in its depositum namespace is the hash of the bytes:
deposit(bytes) -> hash — compute hash(bytes), call apotheca.deposit(hash, bytes). If apotheca returns Collision, that is the expected case for repeat-deposits of identical bytes (the dedup hit); syntheca treats it as success and returns the hash. (A hash collision under different content is a hash-function-strength question outside syntheca's scope; with a collision-resistant hash it does not happen.)get(hash) -> bytes — apotheca's get(hash) directly. Optionally verify on read that the returned bytes hash to the requested name.stat(hash) -> { size, ... } — apotheca's stat(hash) directly.Each item deposited into a syntheca cella lands in apotheca's depositum namespace (the formal noun for a write-once name → bytes pair; entry remains permissible as informal prose). The CAS contract — name derived from bytes — applies to this namespace only.
Apotheca's separate pinax namespace (compare-and-swap name → bytes pairs) is surfaced through syntheca verbatim, with caller-chosen names:
get_pinax(name) -> bytes — apotheca's get_pinax(name) directly.set_pinax(name, bytes, expected) -> Ok | Conflict { actual } — apotheca's set_pinax(name, bytes, expected) directly.The syntheca contract derives the depositum name from the bytes, leaving nothing to compare-and-swap in the depositum namespace; this does not preclude exposing the disjoint pinax namespace, where the apotheca name is caller-chosen and the compare-and-swap primitive is exactly what callers want for state-chain heads and other mutable pointers. Surfacing pinax through syntheca is a pure convenience: it means a caller that holds a syntheca::Cella does not also have to hold an apotheca::Cella over the same root just to read or update a head pointer. syntheca adds no logic on top of the pinax pass-through; verify-on-read does not apply (pinax integrity is single-hash, governed by apotheca's mandatory check).
Everything substrate-level — cella composition, backend list, write-fan-out, local store, one-way sync, encryption wrapper, pinax atomicity and integrity — is inherited from apotheca's cella model. syntheca contributes only the hashing of depositum names, the collision-as-dedup interpretation, and the transparent pinax forwarding.
Once content-addressing is the contract, a global pool of immutable bytes stops being any one project's concern. Any project that segments its content into atoms — sentences, lines, structured records, whatever the unit — can land those atoms in a syntheca cella whose backend list includes a shared remote (a corpus host, a public CDN, an organization-wide bucket); two such projects pointing at the same backend dedupe automatically. The same applies to publishing: any project that produces structured records of a shared format can publish them to a syntheca cella whose backend includes the publish target. Cross-cella dedup falls out of cella composition (apotheca) plus content-addressing (syntheca) with no special protocol — this is a real motivation for the layering, not a side curiosity.
apotheca is the substrate syntheca sits on. apotheca handles bucket management — cella composition, backend trait, multi-backend semantics, write fan-out, local-store layout, one-way sync, write-once enforcement, encryption wrapper, scp/sftp atomicity — plus the compare-and-swap pinax namespace for mutable pointers. syntheca adds CAS semantics on top of the depositum namespace (name = hash of bytes, collision = benign dedup, optional verify-on-read) and forwards the pinax namespace through unchanged.
Anything substrate-level that needs to be specified, decided, or revised lives in apotheca's spec and code, not here. This page covers only what is specific to content-addressing.
Cella location is the caller's choice. The syntheca library opens an apotheca cella at whatever root it is handed; it does not inherit or share apotheca's ~/.apotheca/ default. The reference syn CLI defaults its cella root to ~/.syntheca/, overridable with --cella <PATH>, so syn and apo operate on disjoint stores out of the box.