Skip to content

Bootstrap a Repository

A new repository is built in a fixed order, because each step depends on the one before it:

  1. The repository.
  2. Shapes — the types. A thing can only declare a shape that exists.
  3. Things — the records.
  4. Assertions — claims about those things.

This guide builds a small fleet register: three Vessel things and the Inspection assertions filed against them. It assumes a token in WH_TOKEN and an organization you can create repositories in; see the Python SDK page if you have neither.

repo.create takes the organization, the repository name, and a description. The organization must already exist; a token cannot create one. client.repository("org/repo") then binds a handle, so later calls stop repeating the two names.

from uuid import uuid4
from warmhub import Add, WarmHubClient
client = WarmHubClient.from_env()
org, repo_name = "acme", "fleet"
client.repo.create(org, repo_name, "Vessels and their inspections")
fleet = client.repository(f"{org}/{repo_name}")

shape.create takes the shape name and a mapping of field name to type. This repository needs two:

  • Vessel is an ordinary shape for the records themselves.
  • Inspection is an assertion shape. composes=["Assertion"] is what makes it one: a thing declaring it states its subject in data.about, and WarmHub files it against that subject. On a shape that does not compose Assertion, about would be just another field.
client.shape.create(
org,
repo_name,
"Vessel",
{"length_m": "number", "home_port": "string"},
event_request_id=str(uuid4()),
)
client.shape.create(
org,
repo_name,
"Inspection",
{"verdict": "string", "inspector": "string"},
composes=["Assertion"],
event_request_id=str(uuid4()),
)

event_request_id is a key you generate for each change. If a call fails ambiguously — a timeout, a dropped connection — retry it with the same key. client.shape.list(org, repo_name) lists the new shapes alongside the built-in ones every repository starts with.

See Shapes for the field types a shape can declare.

apply takes a commit message and a list of operations and sends them as one commit. Each Add names the thing, lists the shapes it declares, and carries its data. Payload keys are stored exactly as you write them.

vessels = {
"petrel": {"length_m": 12.4, "home_port": "Falmouth"},
"kittiwake": {"length_m": 9.8, "home_port": "Brixham"},
"fulmar": {"length_m": 15.1, "home_port": "Falmouth"},
}
seeded = fleet.apply(
"register the fleet",
[Add(name=name, shapes=["Vessel"], data=data) for name, data in vessels.items()],
)
print("partial:", seeded.partial)
for row in seeded.operations:
print(row.name, row.status)
partial: False
petrel@v1 applied
kittiwake@v1 applied
fulmar@v1 applied

Check partial. A commit in which some operations are refused does not raise: it returns, the accepted rows stay written, and each refused row carries its error. Only a commit in which every operation fails raises AllStreamOperationsFailedError. The rows come back version-pinned (petrel@v1); use the bare name to read the current version later.

For loads too large to build as one list, see Write Methods and the chunk_size= argument to apply.

An assertion is written like any other thing: an Add that declares the assertion shape, with the subject’s name in data.about.

claimed = fleet.apply(
"record inspections",
[
Add(
name="inspection-petrel-2026",
shapes=["Inspection"],
data={"about": "petrel", "verdict": "pass", "inspector": "harbour-office"},
),
Add(
name="inspection-fulmar-2026",
shapes=["Inspection"],
data={"about": "fulmar", "verdict": "fail", "inspector": "harbour-office"},
),
Add(
name="reinspection-fulmar-2026",
shapes=["Inspection"],
data={"about": "fulmar", "verdict": "pass", "inspector": "surveyor"},
),
],
)

The subject must exist when the assertion is applied: in an earlier commit, as here, or earlier in the same commit. An assertion about a name that does not exist is refused with NOT_FOUND. Any number of assertions can be about the same thing.

head_all collects every current thing that matches, across as many pages as it takes. max_items= is a ceiling: if more rows turn up, it raises VALIDATION_ERROR instead of returning a list larger than you planned for.

thing.about returns the assertions filed against one subject. It lives on client.thing, which takes the organization and repository names explicitly.

current = fleet.things.head_all(shape="Vessel", max_items=len(vessels))
print(sorted(v.wref for v in current))
for assertion in client.thing.about(org, repo_name, "fulmar", limit=25).assertions:
print(assertion.wref, assertion.data["verdict"])
['fulmar', 'kittiwake', 'petrel']
reinspection-fulmar-2026 pass
inspection-fulmar-2026 fail