Documentation
Summaries and indexes
Blobify publishes derived JSON so a frontend can enumerate content, find one entry by a known value, or page through an ordered feed without scanning every content document.
Summary shards
Summary shards are the broad listing layer. Each draft or published state has one manifest and a set of per-model immutable shard files.
spaces/main/summaries/published/index.jsonspaces/main/summaries/published/article/s000.1783796607793.json{privateStoragePrefix}/spaces/main/summaries/draft/index.json{privateStoragePrefix}/spaces/main/summaries/draft/article/s000.1783796607793.jsonPublished-state summaries are public artifacts at the bucket root. Draft-state summaries are authoring artifacts in the private scope under {privateStoragePrefix}/ (the unguessable pv_... folder at the organization root), which the dashboard reaches from authenticated organization context.
The manifest uses format version 2 and maps each logical shard name to its current versioned object key:
{
"formatVersion": 2,
"state": "published",
"models": {
"article": {
"total": 2,
"shardCount": 1,
"shards": {
"s000": "s000.1783796607793.json"
},
"version": 1783796607793
}
}
}Always fetch index.json first, then fetch the file names in the model's shards map. Shard files are immutable. Clients must not guess the current version from a timestamp or build s000.{version}.json themselves.
Shard count is adaptive. Blobify uses 1, 16, or 64 shards based on the size and current tier of the dataset, so consumers must follow the manifest instead of assuming a fixed count.
summaryFields controls which content fields appear. summaryMetadata controls configurable system metadata in published summaries. Draft summaries also include the metadata required by the dashboard, including publishedLocales, publish and update status, and the draft-only updatedBy audit event. Published summaries never expose the editor actor.
The dashboard's content list reads the draft summary manifest and the immutable shard keys it names. Published sites normally read the published state.
Field indexes
Field indexes support exact lookups such as slug to content ID. Configure them with fieldIndexes on a model.
{
"fieldIndexes": [
{ "field": "slug", "normalizer": "slug-v1", "unique": true }
]
}Non-translatable and translatable fields use different logical paths:
spaces/main/lookups/published/article/sku/afa27b44d43b02a9fea41d13cedc2e4016cfcf87c5dbf990e593669aa8ce286d.jsonspaces/main/lookups/published/article/slug/en/afa27b44d43b02a9fea41d13cedc2e4016cfcf87c5dbf990e593669aa8ce286d.jsonPublished value pointer objects are public artifacts at the bucket root. Draft-state pointer objects live in the private scope.
The object key is the SHA-256 (hex) digest of the normalized field value. slug-v1 trims and lowercases its input before lookup. exact-v1 keeps exact-value semantics.
The generated client's getOne(...) reads the value pointer object and then loads the content document. It confirms the pointer ID against the document, and misses fall back to summaries. API upsert uses the same index path, with a content-document scan as a correctness fallback while derived output is still converging.
For unique fields, pointer claims move through pending, live, and tombstone states, with uniqueness enforced at publish time.
Unique fields
Saving a model automatically derives a required field index for each text or slug field marked unique: true. Translatable fields are unique per locale. Non-translatable fields are unique globally.
List indexes
List indexes provide ordered, locale-specific feeds. A model config chooses a field or metadata sort source and an ascending or descending order.
{
"listIndexes": [
{
"id": "latest",
"source": { "kind": "metadata", "name": "lastPublished" },
"order": "desc",
"state": "published"
}
]
}A list index uses one manifest and generational page files:
spaces/main/list-indexes/published/article/latest/en/index.jsonspaces/main/list-indexes/published/article/latest/en/g3/p00000.jsonspaces/main/list-indexes/published/article/latest/en/g3/p00001-v4.jsonPublished-state list indexes are public artifacts at the bucket root. Draft-state list indexes are authoring artifacts in the private scope.
The manifest is the ordered source of truth:
{
"formatVersion": 2,
"model": "article",
"indexId": "latest",
"state": "published",
"locale": "en",
"gen": 3,
"total": 2011,
"pages": [
{ "key": "g3/p00000.json", "count": 1024 },
{ "key": "g3/p00001-v4.json", "count": 987 }
]
}pages[].key is relative to the locale directory and must be treated as opaque. Storage-page counts vary, and the manifest count is authoritative. There is no model pageSize setting for these storage pages, no reverse-shard directory, and no nextPage cursor chain between page files.
The generated client's listByIndex(model, indexId, locale, { page, pageSize }) computes UI pagination across one or more storage pages and returns { items, total, totalPages, page, nextPage }. That runtime pageSize is independent of storage page sizing.
Rebuild derived output
Schema saves do not rebuild existing derived files. After changing summary fields, field indexes, list indexes, display fields, or unique flags on an existing model, run the narrowest rebuild that covers the change.
| Scope | Endpoint |
|---|---|
| All spaces | POST /v1/orgs/{orgId}/schemas/models/{model}/rebuild-summaries |
| All spaces | POST /v1/orgs/{orgId}/schemas/models/{model}/rebuild-field-indexes |
| All spaces | POST /v1/orgs/{orgId}/schemas/models/{model}/rebuild-list-indexes |
| All spaces | POST /v1/orgs/{orgId}/schemas/models/{model}/rebuild-all |
| One space | POST /v1/orgs/{orgId}/content/{spaceId}/{model}/rebuild-summaries |
| One space | POST /v1/orgs/{orgId}/content/{spaceId}/{model}/rebuild-field-indexes |
| One space | POST /v1/orgs/{orgId}/content/{spaceId}/{model}/rebuild-list-indexes |
| One space | POST /v1/orgs/{orgId}/content/{spaceId}/{model}/rebuild-all |
Each endpoint accepts optional ?state=draft or ?state=published. With no state, it rebuilds both. A valid request returns 202 Accepted after enqueueing work. The queue worker performs the rebuild asynchronously.
Content prefixes
If a space sets contentPrefix, Blobify rewrites summary, lookup, and list-index keys under a typed cp_... segment in the public tree:
spaces/main/cp_site_a1/summaries/published/index.jsonspaces/main/cp_site_a1/lookups/published/article/slug/en/afa27b44d43b02a9fea41d13cedc2e4016cfcf87c5dbf990e593669aa8ce286d.jsonspaces/main/cp_site_a1/list-indexes/published/article/latest/en/index.jsonThe private scope never carries a content prefix. The dashboard and generated client handle this automatically.
Pick the right output
- Use a content document when you know the content ID and need full fields.
- Use summaries for broad enumeration and build-time lists.
- Use a field index for an exact lookup by a configured value.
- Use a list index when ordering and scalable UI pagination matter.
Summary fields, field indexes, and list indexes are independent model settings. Including slug in a summary does not create a field index, and adding a field index does not add that field to summaries.