-
Notifications
You must be signed in to change notification settings - Fork 0
Sharing Memories
threadnote share lets a small team keep a curated set of durable memories in a git repository so every member's local agent can recall them. Personal handoffs, preferences, and unpublished durable notes stay local; only memories you explicitly publish leave your machine.
A team is a configured shared git repo. Each team has a name (default: default), a git remote, a local working tree, and a separate gitdir.
| Piece | Location | Notes |
|---|---|---|
| Working tree | ~/.openviking/data/viking/<account>/user/<you>/memories/shared/<team>/ |
Lives inside the OpenViking data tree, so files are addressable as viking://user/<you>/memories/shared/<team>/... and appear in normal recall. |
| Gitdir | ~/.openviking/share/teams/<team>.gitdir/ |
Lives outside the OV data tree so OpenViking never sees git internals. |
| Team config |
~/.openviking/share/teams.json (mode 0600) |
Records the team name, remote, worktree, and gitdir. |
The working tree being inside the memory tree is the whole trick: published files are real Markdown on disk under your user namespace, so they are indexed and returned by recall like any other durable memory, while git operates against the gitdir kept outside the OV tree.
Create the repo first on GitHub/GitLab/etc. and copy its SSH URL, then clone it into your memory tree:
threadnote share init git@github.com:org/team-memories.git
# Optional: add a second team
threadnote share init --team friends git@github.com:you/friends-memories.gitshare init clones the remote into your local memory tree and ingests any existing Markdown memories into OpenViking. It refuses to clone over a non-empty worktree. Switch the default with init --set-default <name>, or just run publish/sync with an explicit --team.
threadnote share list
threadnote share status # default team
threadnote share status --team friends# 1. Identify the personal URI you want to publish (use recall/list as usual).
# 2. Preview the would-be-published bytes before committing:
threadnote share publish viking://user/you/memories/durable/projects/foo/bar.md --preview
# 3. Publish:
threadnote share publish viking://user/you/memories/durable/projects/foo/bar.mdshare publish writes the memory into the team's shared subtree, commits with the message share: publish <relative-path>, pushes, then removes the personal copy after the push succeeds. The memory's recall path becomes viking://user/you/memories/shared/<team>/durable/projects/foo/bar.md.
Before writing, share publish strips supersedes: and archived_from: lines from the memory's header block. Those pointers only resolve on the publisher's machine — teammates pull via git and cannot dereference them — so keeping them would leak local-only provenance into team git history.
Flags:
| Flag | Effect |
|---|---|
--preview |
Read, strip, and scrub the memory and print the exact bytes that would land in git. No writes, no commits, no pushes. Use before any publish to catch leaks by inspection. |
--redact |
Replace soft-leak matches (local home paths) with placeholders and continue. Credentials still block. |
--team <name> |
Target a specific team instead of the default. |
--message "..." |
Override the commit message. |
--no-push |
Commit locally without pushing. |
--dry-run |
Show what would happen without changing anything. |
Do not publish a second copy. Replace the shared URI directly, and Threadnote updates it in place and pushes:
threadnote remember \
--replace viking://user/you/memories/shared/default/durable/projects/foo/bar.md \
--project foo \
--topic bar \
--text "Updated shared knowledge."For MCP, pass the same shared URI as replaceUri to remember_context. Threadnote rewrites the shared memory in place, strips local-only provenance headers, commits, and pushes. You do not store a personal replacement and run share publish again.
threadnote share unpublish viking://user/you/memories/shared/default/durable/projects/foo/bar.mdThe memory is rewritten back into your personal namespace and removed from the shared repo.
threadnote share remove --team friends # deletes worktree + gitdir
threadnote share remove --team friends --keep-files
threadnote share remove --team friends --preserve-local--preserve-local copies shared durable memories back into the personal durable tree before removing the team config. It does not delete remote git history. Without --keep-files, share remove deletes the local checkout. Push any unpushed commits first (threadnote share sync or git -C <worktree> push), otherwise unpublished work is lost.
threadnote share rename --team friends --to platform
threadnote share set-url --team platform git@github.com:org/platform-threadnote.gitshare rename moves the local worktree/gitdir, updates teams.json, and reindexes the shared memory namespace under the new team name. share set-url updates the git origin URL and verifies it with git fetch.
Threadnote does a periodic background git fetch for configured share teams. When an agent calls MCP recall_context / read_context, or the CLI threadnote recall / threadnote read, Threadnote checks whether a shared repo is behind. If it is, Threadnote rebases the clean worktree, reindexes the pulled Markdown into OpenViking, then returns the requested result. Sync errors degrade to warnings so memory access still works with the best local data available.
Crucially, automatic recall/read sync never commits a dirty shared worktree — it warns and leaves that case for explicit threadnote share sync.
Manual sync remains useful to publish local edits, clear a dirty worktree, resolve git conflicts, or force a sync immediately:
threadnote share sync # default team
threadnote share sync --team friends # other team
threadnote share sync --no-push # pull only
threadnote share sync --no-auto-commit # refuse to sync when the worktree is dirtyshare sync auto-commits any uncommitted edits in the worktree, fetches and rebases from the remote, reindexes pulled Markdown into OpenViking (so recall finds them immediately), and pushes.
Only the durable/ kind is shareable. handoffs/, preferences/, incidents/, and other lifecycle kinds stay local by construction — both the initial ingest (share init) and the sync-pull reindex (share sync) skip any file outside durable/.
The publish-time scrubber hard-blocks (never redactable) when it matches any of:
| Pattern | What it catches |
|---|---|
-----BEGIN ... PRIVATE KEY----- |
PEM private key headers |
sk-... (16+ chars) |
OpenAI / Anthropic-style keys (also matches any slug starting sk-; break the pattern by hand on a false positive) |
gh[pousr]_... |
GitHub classic tokens |
github_pat_... |
GitHub fine-grained PATs |
glpat-... |
GitLab PATs |
Bearer ... (20+ chars) |
HTTP bearer tokens |
eyJ... (three base64url segments) |
Bare JWTs, even when the Authorization: Bearer prefix was stripped |
AKIA... |
AWS access keys |
xox[abcdeprs]-... |
Slack bot, user, configuration, legacy cookie, refresh, app, and similar tokens |
The scrubber also blocks on soft-leak patterns that show up routinely in curated memories. These are redactable: pass --redact to replace each match with a generic placeholder and continue. Credentials always block regardless of --redact.
- macOS home paths (
/Users/<you>/...) -><local-path> - linux home paths (
/home/<you>/...) -><local-path>
Other guarantees:
-
Transactional.
share publishwrites and pushes the shared copy first, and only removes the personal copy after the push succeeds. A push failure leaves the personal copy intact; you never end half-published. -
No silent overwrite.
share publishrefuses to overwrite an existing shared memory at the same URI. Usethreadnote remember --replace <shared-uri>(orremember_context({replaceUri:"<shared-uri>"})) for updates, or pick a different topic name. -
Human review still matters. The scrubber is best-effort. Strip the value, preview with
--preview, then publish. -
share publishdeletes the personal copy after publishing. To keep both, copy the memory to a new URI first before publishing.
share sync uses git pull --rebase against the remote. When git cannot merge cleanly:
- The pull reports the conflict and leaves the worktree in a rebase-in-progress state.
- Resolve the conflicts manually in the worktree — it is a normal git checkout.
- Run
git rebase --continue(or--abort) yourself. - Re-run
threadnote share syncto finish the reindex and push.
Two publishes touching the same <topic>.md from different machines will collide. Coordinate ownership per topic, or use distinct topics.
Each user clones into their own user-namespaced path, so identical content shows up under each user's namespace. A memory authored on machine A as viking://user/alice/memories/shared/team/durable/projects/foo/bar.md shows up on machine B as viking://user/bob/memories/shared/team/durable/projects/foo/bar.md. The file content is identical.
supersedes: / archived_from: lines are stripped at the publish boundary so cross-machine URI references do not pollute team git history. Explicit viking:// references inside a body still point at the author's namespace. Prefer narrative references ("see the foo memory under shared/team") over viking:// links in shared content.
The MCP tool share_publish runs the same scrubber as the CLI. It writes and pushes the shared copy first, then removes the personal copy after the push succeeds. Pass an optional team, and "push":false to commit without pushing.
share_publish({"uri":"viking://user/you/memories/durable/projects/foo/bar.md"})
share_publish({"uri":"viking://user/you/memories/durable/projects/foo/bar.md","team":"friends","push":false})
Before publishing, confirm with the user unless they have already authorized you to share durable memories autonomously.
Safety and Security, Memory Lifecycle, Agent Instructions, CLI Reference
GitHub · npm · walkthrough deck · OpenViking · AGPL-3.0-or-later
Start here
Concepts
Workflows
Reference