Patterns
The representation hierarchy shows how two operations scale to arbitrary complexity through conventions at each level. These are the conventions that have emerged so far: value-level, interface-level, and structural.
References
A reference is a value that points somewhere else in the tree.
When a store returns a collection, it doesn't have to embed every item inline. It can return references, lightweight values that say "the real data is over there." A reference is just a map with a path key:
→ {"items": [{"path": "users/alice"}, {"path": "users/bob"}]}
Each reference is a promise: follow the path and you'll find the value. The client decides which references to follow and when. This gives stores control over response size without a separate "fields" or "expand" mechanism.
References can optionally carry type information, a hint about the shape at the other end:
In the hierarchy, references are a value-level convention. They're ordinary map values: no new primitives, no changes to the data model. Any store can return them, any client can detect them. The path in a reference is the same path you'd use to read the value directly.
Shallow by default
Return references instead of embedding. Clients follow what they need. Large responses become small ones.
Consistent addresses
The path in a reference is the same path you'd use to read directly. No indirection, no resolution step.
Convention, not primitive
A reference is an ordinary map. Any store can return them, any client can detect them. The convention is the path key.
Pagination
Collections that are too large for one read. Solved with paths and references, no new mechanisms.
A paginated response includes items and a reference to the next page. The client doesn't construct pagination URLs; it follows the path the store provides:
→ {
"items": [{"path": "users/alice"}, {"path": "users/bob"}],
"links": {"next": {"path": "users/after/bob/limit/2"}}
}
read /users/after/bob/limit/2
→ {
"items": [{"path": "users/charlie"}, {"path": "users/dave"}],
"links": {"self": {"path": "users/after/bob/limit/2"}}
}
When links.next is absent, you've reached the end. The loop is: read a page, process items, follow next.
In the hierarchy, pagination is an interface-level convention. The paginated collection is a struct whose fields (items, links) are backed by behavior; the store computes each page on demand. The client iterates by following references, making it a protocol: an interface through time.
Cursors, not offsets
Offsets break under concurrent modification. Cursors are stable: "everything after this item" stays correct even as the collection changes.
Server-controlled navigation
The store decides cursor shape, page sizes, and orderings. The client just follows references. The store can evolve without breaking clients.
Parameters are paths
Page size, ordering, cursor: all path components. /users/by/name/after/bob/limit/20. Pagination addresses work like every other address.
The meta lens
Sometimes you need to know what a path does before you use it.
For any path P in a store, reading meta/P returns information about that path: what operations it supports, what shape the data takes, what sub-paths are available. The data and its description live in the same tree, accessed by the same operations.
meta/users/alice ← what you can do with it
→ {
"readable": true,
"writable": true,
"paginated": true,
"limits": {"default": 20, "max": 100}
}
This is different from metadata on data. A file's size and modification time describe the data itself: suffix metadata (path/meta). The meta lens describes the path: what the address means, what it affords. The two are orthogonal.
In the hierarchy, the meta lens is structural: it operates across all levels. It can describe a value's type, a struct's fields, an interface's capabilities, or a protocol's state machine. It's introspection over the hierarchy itself.
Prefix, not suffix
meta/path describes capabilities. path/meta describes data properties. Same interface, different questions.
Same interface
Introspection is just reading. No reflection API, no schema registry. If you can read the tree, you can introspect it.
Optionally writable
Writing to a meta path can change the configuration of the underlying path. The lens is a control surface, not just a mirror.
The patterns compose
These aren't independent features. They're layers that stack because they share the same substrate.
References in pages
Paginated responses contain references. The page is an interface; the items are values. Follow what you need.
Meta over pagination
meta/users can describe pagination capabilities (cursor type, max page size, available orderings) before the client makes its first request.
Incremental adoption
Start with inline values. Add references when responses grow. Add pagination when collections grow. Add meta when clients need discovery. Each step is backward-compatible.
Uniform testing
Patterns are data shapes returned by read and write. Test them with the same store mocks you use for everything else.
Thought experiment: 1rec
Mount a service. Any service. Now interact with it through read and write.
The internet already has naming: DNS, URLs, service registries. What it doesn't have is a uniform interaction model. Every service has its own client library, its own pagination scheme, its own error model, its own authentication flow. You need to learn each one separately, test each one separately, and handle each one's failures separately.
Now imagine you mount a proxy store for a weather API at /weather:
→ {"readable": true, "writable": false, "fields": ["location", "forecast", "alerts"]}
read /weather/location/chicago
→ {"temp": 42, "conditions": "cloudy", "forecast": {"path": "weather/location/chicago/forecast"}}
read /weather/location/chicago/forecast
→ {
"items": [{"day": "Mon", "high": 45}, {"day": "Tue", "high": 38}],
"links": {"next": {"path": "weather/location/chicago/forecast/after/Tue/limit/5"}}
}
You didn't import a client library. You didn't read API documentation. Meta told you what the store offers. References connected the current weather to the forecast. Pagination bounded the forecast into pages. The proxy store translated your reads into whatever HTTP calls the weather API actually requires. You never saw them.
Now mount another service. And another. Your tree grows:
├── weather/ ← proxy to weather API
├── github/ ← proxy to GitHub API
├── mail/ ← proxy to IMAP server
└── data/ ← local memory store
Every store responds to the same two operations. Every collection paginates the same way. Every path is introspectable through meta. You can read from /github/repos and write to /mail/drafts in the same session, with the same interface, and swap any of them for a mock without changing a line of application code.
This is 1rec. Not a directory of the internet: DNS already does naming. Not a database that contains everything: the data stays where it lives. 1rec is a single entry point (one record, one tree) where every service you interact with speaks the same interface. The proxy stores do the translation. The patterns do the navigation. You just read and write.
One record. The tree unfolds as far as you want to go.