Browse documentation
Product documentation · snapshot 2026-11-03 · View source ↗

These are the product’s technical docs at this revision. Historical version names and status notes are preserved; see Releases for download qualifications.

Read as Markdown

Agent Memory Protocol v0

On this page

Hubnot memory is deliberate, signed project cognition carried by Git. It is a separate additive protocol, hn-memory/0; memory is not an hn/0 collaboration event and does not alter existing event IDs or actor histories. The implementation is repository-local and experimental.

Identity, objects, and streams

Each memory commit has exactly two mode-100644 files:

memory.json
signature

memory.json is canonical UTF-8 JSON. signature is raw-base64 Ed25519 over those exact bytes. The full public memory ID is sha256:<64-lowercase-hex> over memory.json; it is distinct from the Git commit ID. Actor IDs are 64 lowercase hexadecimal key fingerprints. Stream IDs are full sha256: IDs.

An actor owns each append-only stream:

refs/hn/memory/<full-actor>/<64-hex-stream-digest>
refs/hn/remotes/<remote>/memory/<full-actor>/<64-hex-stream-digest>

The ref owner, payload actor, public-key fingerprint, and stream must agree. Sequence starts at 1 and increases by one. Both the Git parent and signed previous memory ID must name the predecessor. Concurrent append failure asks the writer to reload and retry; streams never merge.

Envelope and record model

Every envelope names protocol, operation, actor, actorName, publicKey, stream, sequence, and an RFC 3339 timestamp. Later stream entries also name previous. The operations are:

Operation Meaning
record Create a record.
supersede Create a replacement and point to a record by the same actor.
retract Append a typed retraction of a record by the same actor.
challenge Append another actor's typed dispute, optionally with evidence.

Records have exactly one kind: observation, decision, assumption, attempt, verification, or handoff. Attempts require an outcome; verifications require typed evidence. Handoffs keep completed, assumptions, blockers, and nextActions as four separate bounded lists. Proposed next actions are inert data, never permission to execute them.

Every record has an exact commit anchor. Optional path anchors bind a repository-relative path to its exact blob ID, or to absent. Optional typed subjects bind full issue:, proposal:, event:, policy:, pipeline:, or run: IDs. Applicability is exact, descendants, or subject. Evidence IDs are explicitly typed as git:<full-object-id>, event:sha256:<full-event-id>, or memory:sha256:<full-memory-id>.

Content is limited to 65,536 UTF-8 bytes, topics to 32 unique normalized labels, evidence to 64 unique IDs, paths to 128 entries and 65,536 aggregate path bytes, and each handoff list to 64 entries with a 65,536-byte aggregate. Version 0 permits no attachments.

Lifecycle and the five independent judgments

Projection consumes the verified set and sorts exact IDs; timestamp or arrival order never chooses a winner. One successor makes the target superseded. Multiple successors make it branching. A valid retraction makes it retracted; challenges remain visible alongside lifecycle state. Missing targets become explicit dependencies rather than silently disappearing.

Keep these judgments separate:

  1. Signature validity proves attribution and byte integrity.
  2. Policy qualification says the exact actor and kind are listed by policy.
  3. Evidence resolution says named objects are available and structurally valid.
  4. Semantic truth is a human or agent judgment the protocol cannot prove.
  5. Prompt authority is never granted by a memory, handoff, signature, or selection.

Policy qualification

The optional memory section of .hn/policy.json is evaluated from the exact query commit:

{
  "memory": {
    "trustedActors": ["<full-actor-fingerprint>"],
    "trustedKinds": [
      "assumption",
      "attempt",
      "decision",
      "handoff",
      "observation",
      "verification"
    ]
  }
}

Both arrays must be sorted and unique. Without the section, valid memory is policy-missing: it is available to explicit inspection but excluded from default recall. --include-untrusted reveals the real non-qualifying class; it does not upgrade it.

Record, correct, and hand off

hn memory record --kind decision --at HEAD --applies descendants \
  --topic architecture --evidence git:$(git rev-parse HEAD) \
  --content "Keep memory streams separate from collaboration actor chains."

hn memory supersede sha256:<full-memory-id> \
  --kind decision --at HEAD --applies descendants \
  --content "Replacement decision with current rationale."

hn memory retract sha256:<full-memory-id> --reason incorrect
hn memory challenge sha256:<another-actor-memory-id> \
  --reason evidence-mismatch --evidence git:$(git rev-parse HEAD)

hn memory handoff --at HEAD --applies descendants --input handoff.json --json

Machine record and handoff requests are strict version-0 JSON. For handoff input, --at and --applies supply omitted anchor/applicability context; if the JSON also supplies either field it must match exactly. Other record flags cannot be combined with --input. Unknown or duplicate fields, conflicts, invalid UTF-8, and oversized input fail before append. Hubnot reads only explicitly supplied fields; it does not capture prompts, responses, terminal history, environment, clipboard, credentials, or unrelated working-tree files.

Bounded recall

hn memory recall --at HEAD --topic architecture --query "stream isolation" --json
hn memory recall --at HEAD --include-untrusted --lifecycle all --json
hn memory show sha256:<full-memory-id> --json

Recall filters exact commit, subject, path, topic, kind, actor, lifecycle, trust, and deterministic lexical terms. Defaults are active lifecycle, policy-qualified trust, at most 20 records, and at most 65,536 bytes of encoded data.content. Stable order is applicability class, lifecycle class, signed timestamp descending, then full memory ID. Every result carries full memory, actor, and stream IDs; anchor; signature; lifecycle edges; applicability; evidence details; trust class; content digest; and nested inert data.

JSON always places the constant warning outside author data. Author text stays beneath memories[].data.content, and handoff fields stay beneath memories[].data.handoff. Continuation cursors bind the normalized filters, bounds, exact policy digest, and accepted-source fingerprint. Any change to sources, policy, filters, query, commit, or bounds invalidates the cursor. Recall and grep use native Git subprocesses to read and verify repository objects and may rebuild their disposable verified index. They perform no explicit Hubnot sync/fetch, memory author-command execution, memory-driven tool action, policy change, event append, or ref update.

In a configured partial clone, Git's promisor behavior may lazily fetch a missing object, including the .hn/policy.json blob, during those reads. On Git versions that support it, GIT_NO_LAZY_FETCH=1 enforces an offline read for either command; verification may fail when required objects are unavailable. For example: GIT_NO_LAZY_FETCH=1 hn memory grep --json 'stream[-_]isolation'. Complete repositories read their existing local objects. Trust and source verification remain unchanged in either case.

Explicit regex fallback

When lexical recall misses a spelling, identifier fragment, or punctuation pattern, an agent may explicitly choose regex search:

hn memory grep --at HEAD --topic architecture --json 'stream[-_ ]isolat.*'
hn memory grep --ignore-case --max-records 5 'failure|timeout'
hn memory grep --json '(?m)^Decision:'
hn memory grep --json -- '-legacy[0-9]+'

hn memory grep [filters] [bounds] [-i|--ignore-case] [--json] PATTERN accepts exactly one positional pattern. Put flags before the pattern; use -- when it starts with a hyphen. Quote patterns so the shell passes them literally. It uses Go's standard-library regexp syntax (RE2-style), including Unicode classes such as \p{Han}; lookaround and backreferences are unsupported. Matching is case-sensitive by default. -i and --ignore-case enable Unicode simple case folding; inline (?i) and scoped flags follow Go regexp semantics. ^ and $ anchor the whole field by default; (?m) anchors each line. . excludes newlines by default; (?s) includes them. Empty patterns match every eligible record. This is an explicit fallback: recall --query continues to mean lexical terms and never automatically interprets regular expressions.

Each candidate's complete data.content, each topic, and each string in handoff completed, assumptions, blockers, and nextActions are searched separately. A match in any field returns the complete record once. Matching never crosses field or list-item boundaries. Evidence references, provenance, IDs, generated warnings, and classification metadata are outside this search surface. Human output identifies each matching field with a generated label and an escaped value, including matches found only in structured handoff fields.

Grep shares recall's filters (--at, --subject, --path, repeated --topic, --kind, --actor, --lifecycle, --trust, and --include-untrusted), defaults, verified sources, applicability and resolved-evidence requirements, deterministic ordering, --max-records, and --cursor. For grep, --max-content-bytes counts the complete JSON encoding of each returned data object, including content, topics, handoff strings, and other author-supplied record fields. Records remain atomic: if any match exceeds that bound, the query fails before emitting a page. Lexical recall retains its existing data.content-only byte budget. Grep emits human output or the same version-0 JSON result envelope, including full provenance and inert author data. It has no --query or --input mode; existing recall machine requests are unchanged. Grep cursors additionally bind a separate regex domain, exact pattern bytes, and ignore-case flag, and cannot be reused for lexical recall or another regex/flag combination.

Patterns must be valid UTF-8 and at most 4,096 bytes. A conservative syntax-tree expansion estimate (including fixed program overhead) and the compiled program must each fit 4,096 Go regexp instructions. The estimate is checked before expanding counted repetitions. Invalid, unsupported, and over-complex patterns fail before any index rebuild. Before matching, the complete eligible selection must fit a deterministic work limit of 67,108,864 units: compiledInstructions × sum(fieldUTF8Bytes + 1) over all searchable fields. Exceeding this limit fails without emitting a partial result; narrow filters or simplify the pattern. Pagination bounds output rather than the scanned corpus. Grep uses the existing verified private index; it creates no text export and requires no external grep, shell evaluation, or additional runtime.

Disposable local index

hn memory index rebuild
hn memory index verify

The private JSON index is .git/hn/memory/index-v0.json. Its source fingerprint binds the exact policy digest plus sorted (ref, head) pairs. Directories are owner-only and the file is mode 0600; symlinks, unknown fields, malformed records, incompatible versions, and stale sources fail closed. The index is derived, noncanonical, and never transported. Delete it at any time and rebuild from verified local and accepted refs.

Select and synchronize

hn replication select origin \
  --memory sha256:<full-stream-id> \
  --max-events 10000 \
  --max-objects 30000 \
  --max-total-bytes 134217728
hn sync origin

Selection authorizes transport, not trust. Exact streams enter a separate bare quarantine, are measured and validated, and promote only to accepted remote refs. Actor, proposal, and memory selectors have independent outcomes. An explicit actor-only selection imports no memory. --all discovers all three namespaces but remains mutually exclusive with exact selectors.

hn sync also publishes local memory refs with explicit Git refspecs; it does not publish the primary branch. In a fresh clone, save exact stream selectors, sync, then rebuild. The clone receives neither another actor's private key nor the publisher's index and cannot author as that actor. Exact selected shallow recovery is available through hn sync origin --recover-shallow; it never globally unshallows or silently adds a selector.

Limits

Version 0 has no federation, automatic transcript capture, embeddings, semantic truth engine, autonomous action, provider service, moderation, redaction, retention enforcement, or erasure guarantee. Retraction and local index deletion do not erase Git objects already replicated elsewhere. See the memory safety model, replication protocol, and repeatable direct host observations, including the dated public two-actor memory proof with full IDs and OIDs.