Bootstrap a Repository
A new repository is built in a fixed order, because each step depends on the one before it:
- The repository.
- Shapes — the types. A thing can only declare a shape that exists.
- Things — the records.
- 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.
1. Create the repository
Section titled “1. Create the repository”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}")2. Define the shapes
Section titled “2. Define the shapes”shape.create takes the shape name and a mapping of field name to type.
This repository needs two:
Vesselis an ordinary shape for the records themselves.Inspectionis an assertion shape.composes=["Assertion"]is what makes it one: a thing declaring it states its subject indata.about, and WarmHub files it against that subject. On a shape that does not composeAssertion,aboutwould 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.
3. Write the things
Section titled “3. Write the things”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: Falsepetrel@v1 appliedkittiwake@v1 appliedfulmar@v1 appliedCheck 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.
4. Write the assertions
Section titled “4. Write the assertions”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.
Read it back
Section titled “Read it back”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 passinspection-fulmar-2026 failNext steps
Section titled “Next steps”- Paginate with the
*_itermethods to read larger sets. - Keep a local cache with change drains to follow the repository as it changes.
- Assertions covers what an assertion means and how to query it.