Files
homelabstack/.claude/skills/vault/SKILL.md
T

113 lines
3.9 KiB
Markdown

---
name: vault
description: Read and write notes in ginnoir's self-hosted Obsidian vault (CouchDB obsidian-livesync backend at obsidian.ginnoir.com). Use when loading persistent context for a session, saving new context, writing a note, or querying vault contents. The vault is the canonical persistent-context store across Claude sessions.
---
# vault
Obsidian vault backed by CouchDB (obsidian-livesync) at `obsidian.ginnoir.com`.
## Credentials
```
Endpoint : http://admin:couchdb_obs_p4ss_7x9k@localhost:5984 (via docker exec couchdb)
DB : obsidian
External : https://obsidian.ginnoir.com (also in stacks/notes/stack.env)
```
## Document format (confirmed from live vault)
obsidian-livesync uses a **two-level structure**:
**File doc**`_id` is the vault-relative path (e.g. `testing.md`, `claude/Context.md`):
```json
{
"_id": "testing.md",
"path": "testing.md",
"type": "plain",
"children": ["h:1w5waerza6mc9"],
"ctime": 1780735543814,
"mtime": 1780735679684,
"size": 14,
"eden": {}
}
```
**Leaf/chunk doc**`_id` is `h:<short-hash>`, holds the actual content:
```json
{
"_id": "h:1w5waerza6mc9",
"data": "this is a test",
"type": "leaf"
}
```
Content lives in leaf docs. `children[]` lists chunk IDs in order (concatenate for large notes).
Slashes in paths must be URL-encoded as `%2F` in requests.
## Read all notes (list)
```powershell
ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/_all_docs?include_docs=true"'
```
File docs: `type == "plain"`, `_id` does not start with `h:` or `obsydian_`.
## Read one note's content
```powershell
# 1. Get file doc — find chunk IDs in children[]
ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/testing.md"'
# 2. Fetch chunk — content is in .data
ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/h%3A1w5waerza6mc9"'
```
## Write a new note
Write the leaf first, then the file doc. Use a fixed chunk ID so you can update it later.
```bash
ssh -o BatchMode=yes ginnoir@valhalla '
B="http://admin:couchdb_obs_p4ss_7x9k@localhost:5984"
NOW=$(date +%s%3N)
CHUNK="h:claude_ctx_1"
CONTENT="# My Note\n\nContent here."
docker exec couchdb curl -sf -X PUT "$B/obsidian/$CHUNK" \
-H "Content-Type: application/json" \
-d "{\"data\":\"$CONTENT\",\"type\":\"leaf\"}"
docker exec couchdb curl -sf -X PUT "$B/obsidian/claude%2FContext.md" \
-H "Content-Type: application/json" \
-d "{\"path\":\"claude/Context.md\",\"type\":\"plain\",\"children\":[\"$CHUNK\"],\"ctime\":$NOW,\"mtime\":$NOW,\"size\":${#CONTENT},\"eden\":{}}"
'
```
## Update an existing note
Fetch `_rev` for both the file doc and chunk doc, then re-PUT both with `_rev` included.
For complex content (markdown with quotes/newlines), write JSON to `/tmp/` on valhalla, `docker cp` into container, and `curl -d @/tmp/file.json` — avoids shell escaping.
## Load context at session start
```powershell
# Get all file docs and their chunk content
ssh -o BatchMode=yes ginnoir@valhalla 'docker exec couchdb curl -sf "http://admin:couchdb_obs_p4ss_7x9k@localhost:5984/obsidian/_all_docs?include_docs=true"' | python -c "
import sys, json
d = json.load(sys.stdin)
chunks = {r['id']: r['doc']['data'] for r in d['rows'] if r['id'].startswith('h:')}
for r in d['rows']:
doc = r['doc']
if doc.get('type') == 'plain' and not doc.get('deleted'):
content = ''.join(chunks.get(c, '') for c in doc.get('children', []))
print(f'=== {doc[\"_id\"]} ===')
print(content)
"
```
## Notes on valhalla Python
`python3` on the valhalla *host* cannot resolve `localhost` (DNS issue in SSH env). Always use `docker exec couchdb curl` — the container resolves localhost fine. On the Windows Claude Code host, use `python` (not `python3`).