Documentation
¶
Overview ¶
Package vault reads and writes notes in the Obsidian vault.
The markdown file is the source of truth. Reading is forgiving — anything it cannot parse is preserved by simply not touching it. Writing is rare and deliberate: see WriteAtomic, the single door through which anything enters the vault.
Index ¶
- Constants
- func EnsureDir(path string) error
- func MkdirPrivate(dir string) error
- func NormalizeLink(target string) string
- func Path() string
- func Private(path string) error
- func PrivateSiblings(path string) error
- func Record(dir string) error
- func Recorded() string
- func Slug(vaultDir, path string) string
- func WriteAtomic(path string, data []byte) error
- type Edge
- type EdgeSrc
- type Guard
- type Note
Constants ¶
const ( // FileMode is owner read/write, nothing for anyone else. Every file this // product creates inside the vault. FileMode os.FileMode = 0o600 // DirMode is owner-only traversal. A directory that is group- or // world-executable lets anyone walk in and stat what is inside, which for a // vault leaks the project names and session times even when every file in // it is unreadable. DirMode os.FileMode = 0o700 )
The vault holds the user's private memory: what they told an assistant, who they work with, and every prompt they typed. It lives in their home directory, which on a shared machine is readable by everyone with an account unless something says otherwise.
Nothing did. Notes came out 0600 only because os.CreateTemp happens to make files that way and WriteAtomic renames one into place — while the two files that hold *everything*, the activity log and index.db, were written 0644 by their own callers. So the careful-looking permission on the markdown was undone by a world-readable database containing the same text.
These constants make the intent explicit rather than incidental, so a new writer inherits it instead of rediscovering it.
Variables ¶
This section is empty.
Functions ¶
func MkdirPrivate ¶ added in v0.1.3
MkdirPrivate creates a directory the way the vault wants it.
os.MkdirAll does not change the mode of a directory that already exists, so this does not silently retighten a vault the user set up deliberately — new directories are private, and existing ones are left as they are and reported by `brain doctor` instead. Tightening someone's filesystem without asking is its own kind of surprise.
func NormalizeLink ¶
NormalizeLink reduces a wikilink target to its trailing segment. Full entity resolution lands in step 3; this is the cheap normalisation that makes [[Sameer Rao]] and [people/sameer-rao] agree.
func Path ¶ added in v0.1.4
func Path() string
Path resolves where the vault lives. One answer, for every front end.
The order is explicit override, then recorded choice, then default:
- BRAIN_VAULT, which is how an MCP host config pins a server to one vault and how a scratch vault is used in a test or a shell. An explicit instruction in the current process wins over anything on disk.
- The path `brain setup` recorded. This is what makes the desktop app find a non-default vault, since it has no environment to inherit.
- ~/brain. Absolute, because a relative default is resolved against whatever directory a host happened to launch the binary from — which is the original version of this bug.
func Private ¶ added in v0.1.3
Private sets FileMode on a path that already exists, ignoring absence.
For files this package does not create itself — index.db, which the SQLite driver opens with its own mode. Ignoring a missing file matters because the caller uses this on paths the driver creates lazily.
func PrivateSiblings ¶ added in v0.1.3
PrivateSiblings applies Private to a path and the sidecars a SQLite database keeps beside it. The -wal file is the one that matters most: it holds pages not yet checkpointed back, which is to say the most recent writes.
func Record ¶ added in v0.1.4
Record writes down which vault this machine uses, so a front end with no environment to inherit can still find it. Called by `brain setup` once the vault is known.
Failure is returned rather than swallowed, but callers treat it as a warning: a machine where the config directory cannot be written still has a working vault, it just has one the desktop app can only find if it is at the default.
func Recorded ¶ added in v0.1.4
func Recorded() string
Recorded returns the vault path written by Record, or "" if there is none.
A recorded path that no longer exists is ignored rather than returned. The pointer is a convenience, not a second source of truth: if someone moved or deleted their vault, falling back to the default and creating a fresh one is wrong, but so is insisting on a directory that is not there — the caller gets the default and the "no vault at …" message that names it.
func WriteAtomic ¶
WriteAtomic writes via a temp file in the same directory then renames, so a crash mid-write cannot leave a truncated note and Obsidian never observes a partial file.
Every path that puts a file into the vault goes through here — the rollup applying an accepted proposal, and an agent committing a checkpoint. One writer means one set of guarantees, rather than each caller reinventing how carefully it wants to be interrupted.
Types ¶
type EdgeSrc ¶
type EdgeSrc string
EdgeSrc records where an edge came from, which determines how much we trust it.
type Guard ¶ added in v0.2.0
type Guard struct {
// contains filtered or unexported fields
}
Guard is a held lock. Unlock is safe to call exactly once, and callers defer it immediately.
func Lock ¶ added in v0.2.0
Lock takes the vault-wide lock named name, blocking until it is free.
name is a bare identifier — "memory-fact", "memory-pending" — so that writers to different files do not queue behind one another. Callers that need two locks must take them in a fixed order; today the only nesting is a kind lock outside the pending lock.
A vault on a filesystem with no working flock (some network mounts) is not a reason to refuse to save a memory, so the OS half degrades to the in-process half and says so on stderr rather than failing the write. It is announced because a silently weaker guarantee is how this class of bug got here.
type Note ¶
type Note struct {
Slug string
Path string
Title string
Kind string
Aliases []string
Body string
Edges []Edge
Hash string // content hash, so reindexing only re-embeds what changed
// FirstSeen is a unix timestamp parsed from frontmatter, 0 if absent. It is
// what the graph's time scrubber animates over: when each note entered the
// vault.
FirstSeen int64
}