Skip to main content
FactBlock is the format tckg reads and writes, and factblock is its library and CLI. A bundle is a folder of JSONL files (factblock.json, nodes.jsonl, edges.jsonl, resolutions.jsonl) that answers as-of reads with no server. tckg is the same thing hosted: a server that stamps known_at, keeps spaces apart, and serves search, why, resolve and MCP. The rule for which one to reach for: what runs on your folder and your own model key is factblock; what needs the server’s clock, other people’s data, or an MCP address is tckg.

The loop

sync is two-way by identity (node id; edge source, target, type and asserted_at). Rows only the folder has go up under the folder’s own batches, so a 2024 transcript is known here as of 2024, not as of the day you pushed it. Rows only the ledger has come down with the ledger’s known_at. A second run does nothing. --push-only and --pull-only do half; --pull-only into a folder that does not exist clones a space. Verdicts (resolutions) are never pushed: they are the ledger’s to make.

Extract here or on the server

Both run the same claims profile (the instructions, output schema and edge types live in the factblock repo and tckg mirrors them).

Two clocks, and when to use --backfill

observed_at is when the words were said; it becomes every block’s asserted_at. known_at is when you learned it.
  • Material from the past (an old transcript, an archive): --backfill. Known when it was said, so as-of reads in the past see it. This is a declaration, recorded in the bundle as a backfill batch, and the certificate counts it.
  • Something you just learned: leave it off. Known now.
Never set known_at or attestation on a row yourself. A writer that is not a ledger declares a batch; that is the only way a past known_at is honest.

Spaces and keys

Rows that carry a space keep it on push; the rest take --space. A pull without --space brings the whole tenant.

Mistakes the format will not stop

  • lag must be one duration. 10d, 2 weeks, P1M. A range like 1-2 months would be read by Postgres as 1 year 2 months, so tckg refuses it (lag_not_a_duration). Keep ranges and words in properties.lag_text.
  • Node ids are unique per tenant. The same claim in two worldviews is two nodes with two ids; link back to the shared record through payload (factagora.ai uses payload.factblock_id).
  • A changed belief is a new node plus SUPERSEDES, not an edit. There is no delete; the old belief is the history.

Moving a whole store

Importing an existing database is the same loop with a script in place of extract: write the rows to a bundle with one backfill batch per day, validate, sync --push-only. Migrate from Supabase does this for factagora.ai’s TKGs. Any other store can do the same by implementing factblock’s two-method Store protocol (pull, push); TckgStore is the reference.