> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tckg.factagora.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Work with files (factblock)

> The open-source factblock library is tckg's file format and its local client. Extract on your machine, check the folder, then push it; pull a space back to read it offline.

[FactBlock](https://github.com/factagora/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.**

```bash theme={null}
pip install --pre factblock      # alpha: --pre is required
factblock sample brain/          # a small bundle to try every command on
```

## The loop

```bash theme={null}
# 1. text in, blocks out, on your machine (your model key)
factblock extract call.txt --observed-at 2026-09-30T14:00:00Z --speaker "Jerome Powell" \
  --source-name "FOMC press conference" --provider gemini --backfill -o brain/

# 2. check it before anything is written anywhere: 11 ok
factblock validate brain/
factblock scan brain/ --as-of 2026-10-01

# 3. push to a space, pull what others added
export TCKG_TOKEN=...            # the tenant's key
factblock sync brain/ https://tckg.factagora.com --space user:randy

# 4. read offline, from the folder
factblock recall brain/ "rate cuts" --as-of 2026-10-01
factblock why brain/ <block-id> --as-of 2026-10-01
factblock to-parquet brain/ brain-pq/      # DuckDB, Spark
```

`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).

| | `factblock extract` | `messages` on [`POST /v1/memories`](/api-reference/write-memories) |
| - | - | - |
| Model | Yours: `gemini`, `openai`, or `fake` (no model, for trying the format) | tckg's Gemini on Vertex |
| You see the result | Before it is written. Edit, drop, re-run | After it is written. Nothing is deleted |
| Many dated texts | One call: a `.jsonl` with `{text, observed_at, speaker?, source?}` per line, one batch per line | One request, several messages |
| Use it for | Backfills, research, anything you want to review first | Live app traffic (an ingest route) |

## 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

| | Value |
| - | - |
| A person's or agent's worldview on factagora.ai | `tckg:<tkgs.id>`, one space per TKG |
| Your own scratch memory | `user:<name>` |
| An agent's own memory | `agent:<id>` |
| Keys | One per app: factagora.ai and app.factagora.com are separate tenants and never see each other's rows. Keys are issued with `deploy/gcp/keys.sh` in the tckg repo and handed out through the password manager; never paste one into a chat or a commit |

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](/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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.