Skip to content

Work with objects (structured records) from the CLI

CLI onlyIntermediate~15 min

The hadron object command group is the collection-oriented CRUD-and-query surface over structured storage. An object is a node with an objectType, presented as a flat record { id, type, ...fields } — so you work with typed records, not graph nodes.

Reach for it when a memory holds many records of the same shape (competitors, invoices, incidents) and you want database ergonomics from the terminal: create / get / update / find / delete. This guide walks all five on a competitor collection — the same example as the market-research tutorial. The same object store is on GraphQL and MCP too — see Object store API.

Before you start

  • The hadron CLI, installed and signed in — see Install the hadron CLI.
  • A memory to write to. The examples use acme.com::market.
  • Recommended: a declared schema on the competitor collection (see Give a memory a structured schema). With a schema, writes are validated against the collection; without one, objectType and fields are free-form.

The flat-record model

An object is { id, type, ...fields }:

  • id and type are the reserved envelope — they can't be field names.
  • The node's typed properties are the top-level fields.
  • loc and name are auto-derived and hidden.

--key sets a human-meaningful natural id — a single loc segment, no : — and you can omit it for a server-generated id. So --type competitor --key letta lands at node loc competitor:letta, addressable by the node URN acme.com::market::competitor:letta.

1. Create

object create takes the collection (--type) and the fields as a JSON object (--fields, or --fields-file to read from a file):

hadron object create -m acme.com::market --type competitor \
  --fields '{"name":"Letta","stage":"series-a","fundingUsd":12000000}' \
  --key letta --json
{ "id": "019f7d…", "type": "competitor", "name": "Letta", "stage": "series-a", "fundingUsd": 12000000 }

On a schema-governed memory the fields are validated against the collection — a missing required field, an out-of-enum value, or a wrong type is rejected before anything is written. Omit --json for human-readable output; --name overrides the auto-derived node name.

2. Read — get

Read one object by its id (the value printed on create) or its node URN:

hadron object get 019f7d… --json
hadron object get acme.com::market::competitor:letta --json

Both print the same flat record. A ref that names nothing readable — or a node that isn't an object — is a clean not-found (exit code 4), not an error.

3. Update — a shallow merge

object update merges the fields you pass into the existing record: the patch wins on a key collision, and unmentioned fields are preserved. The merge is atomic server-side.

hadron object update 019f7d… \
  --fields '{"stage":"series-b","fundingUsd":40000000}' \
  --reason "Series B announced" --json
{ "id": "019f7d…", "type": "competitor", "name": "Letta", "stage": "series-b", "fundingUsd": 40000000 }

name survived because the patch didn't mention it. --reason is recorded in the object's revision history.

Merge, not replace

This is the key difference from node update --properties, which replaces the whole properties bag. object update --fields merges. If you want replace semantics, drop to the node layer.

The merged result is re-validated against the schema, so a merge can't leave a record in a non-conforming state.

4. Find — query a collection

object find (alias ls) queries one collection and prints the matching objects plus a total. Three ways to filter:

  • --match — an equality shorthand: a JSON object { field: value, … }, ANDed into an eq predicate per field (the comparison type is inferred from the schema).
  • --where — the full structured predicate, the same JSON grammar as search --where. AND-combined with --match.
  • --sort — a single-field shorthand: { "<field>": "asc" | "desc" }.

The equality shorthand:

hadron object find -m acme.com::market --type competitor \
  --match '{"stage":"series-a"}'

The full predicate — series-A competitors funded over 10 million, highest first (the market-research payoff query, from the terminal):

hadron object find -m acme.com::market --type competitor \
  --where '{"and":[
             {"path":["stage"],"eq":"series-a"},
             {"path":["fundingUsd"],"as":"number","gt":10000000}
           ]}' \
  --sort '{"fundingUsd":"desc"}' --json

--limit and --offset page the results.

5. Delete

object delete (alias rm) is soft by default — the object disappears from reads but the row is retained. --hard removes the row permanently. Deletion is non-recursive; an object is a single record.

hadron object delete 019f7d… --yes
hadron object rm acme.com::market::competitor:letta --hard --yes

--yes skips the confirmation prompt (use it in scripts and agent runs).

Which layer? object vs. node

object and node write to the same substrate — an object is a node with an objectType. Pick by what you're doing:

Reach for hadron object when… Drop to hadron node / hadron search when…
You want flat typed records — CRUD by field, query by value. You need the graph: edges, loc hierarchy, or non-record nodes.
You want merge-on-update (update --fields). You want replace-on-update (node update --properties).
The record is self-contained. The node carries content/markdown, edges, or children.

The node-level structured surface (node --object-type / --properties, search --where / --sort-property) is documented in the hadron CLI reference and Structured storage and queries.